目录

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窗口进行重排,新暴露的窗口的可见区域需要进行重绘。

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

MarkOverlappedWindows

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

1// pWin为需重排的窗口,pFirst为第一个改变显示的窗口,pWin和pFirst为同一层窗口 2// pWin窗口向下移,pFirst为窗口原堆叠层的pWin的下一个相邻窗口(pWin->nextSib),标记与pWin重叠的窗口 3// pWin窗口向上移,pFirst为pWin本身窗口,标记被pWin遮挡的窗口 4Bool miMarkOverlappedWindows(WindowPtr pWin, WindowPtr pFirst, WindowPtr *ppLayerWin){ 5 BoxPtr box; 6 WindowPtr pChild, pLast; 7 Bool anyMarked = FALSE; 8 MarkWindowProcPtr MarkWindow = pWin->drawable.pScreen->MarkWindow; 9 if (ppLayerWin) 10 *ppLayerWin = pWin; 11 12 if (pWin == pFirst) { 13 // Blindly mark pWin and all of its inferiors. This is a slight overkill 14 // if there are mapped windows that outside pWin's border, 15 // but it's better than wasting time on RectIn checks. 16 pChild = pWin; 17 // 深度优先遍历,初始化该窗口及其子窗口的窗口和边框区域 18 // 子窗口的区域限制在pWin的窗口(winSize)区域内 19 while (1) { 20 if (pChild->viewable) { 21 // 根据窗口的drawable构建winSize区域,该区域限制在父窗口的winSize范围内 22 if (RegionBroken(&pChild->winSize)) 23 SetWinSize(pChild); 24 // 包括窗口边框的区域,该区域限制在父窗口的winSize范围内 25 if (RegionBroken(&pChild->borderSize)) 26 SetBorderSize(pChild); 27 // miMarkWindow, 构建Validate对象,记录起始坐标 28 (*MarkWindow) (pChild); 29 if (pChild->firstChild) { 30 pChild = pChild->firstChild; 31 continue; 32 } 33 } 34 while (!pChild->nextSib && (pChild != pWin)) 35 pChild = pChild->parent; 36 // pWin的子窗口遍历完成,退出循环 37 if (pChild == pWin) 38 break; 39 pChild = pChild->nextSib; 40 } 41 // pWin窗口向上移,pWin为第一个暴露的窗口,可能需要重绘,因此需标记 42 anyMarked = TRUE; 43 // 下一个相邻窗口 44 pFirst = pFirst->nextSib; 45 } 46 if ((pChild = pFirst)) { 47 // pWin区间,窗口向上移,该区域会遮挡相邻窗口,向下移,则会暴露相邻窗口 48 box = RegionExtents(&pWin->borderSize); 49 pLast = pChild->parent->lastChild; 50 while (1) { 51 if (pChild->viewable) { 52 if (RegionBroken(&pChild->winSize)) 53 SetWinSize(pChild); 54 if (RegionBroken(&pChild->borderSize)) 55 SetBorderSize(pChild); 56 // 区域有重叠,这标记该窗口,可能被遮挡,也可能会被暴露 57 if (RegionContainsRect(&pChild->borderSize, box)) { 58 // 调用miMarkWindow 59 (*MarkWindow) (pChild); 60 anyMarked = TRUE; 61 if (pChild->firstChild) { 62 pChild = pChild->firstChild; 63 continue; 64 } 65 } 66 } 67 while (!pChild->nextSib && (pChild != pLast)) 68 pChild = pChild->parent; 69 if (pChild == pLast) 70 break; 71 pChild = pChild->nextSib; 72 } 73 } 74 if (anyMarked) 75 (*MarkWindow) (pWin->parent); 76 return anyMarked; 77}

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)等

1int miValidateTree(WindowPtr pParent, WindowPtr pChild, VTKind kind) { 2 // Total clipping region available to the marked children. 3 // pParent's clipList merged with the borderClips of all the marked children. 4 RegionRec totalClip; 5 RegionRec childClip; /* The new borderClip for the current child */ 6 RegionRec childUnion; /* the space covered by borderSize for all marked children */ 7 RegionRec exposed; /* For intermediate calculations */ 8 WindowPtr pWin; 9 Bool overlap; 10 int viewvals; 11 Bool forward; 12 13 ScreenPtr pScreen = pParent->drawable.pScreen; 14 if (pChild == NullWindow) 15 pChild = pParent->firstChild; 16 RegionNull(&childClip); 17 RegionNull(&exposed); 18 19 // compute the area of the parent window occupied by the marked children + the parent itself. 20 // This is the area which can be divied up among the marked children in their new configuration. 21 RegionNull(&totalClip); 22 viewvals = 0; 23 // 计算可能变化的可见区域(totalClip),有两种方法: 24 // 1. 父窗口的borderClip区域减去pChild之上的子窗口区域(boarderSize),这些子窗口区域的显示内容不会改变 25 // 2. 所有与移动窗口有重叠的子窗口的可见区域之和,为父窗口的最大可见区域,这些区域的内容会发生变化 26 if (RegionBroken(&pParent->clipList) && !RegionBroken(&pParent->borderClip)) { 27 // RegionBroken表示数据空间为空,一般来说根窗口的clipList置为broken,因为根窗口时常变化。 28 kind = VTBroken; 29 // When rebuilding clip lists after out of memory, assume everything is busted. 30 forward = TRUE; 31 RegionCopy(&totalClip, &pParent->borderClip); 32 RegionIntersect(&totalClip, &totalClip, &pParent->winSize); 33 // 在pChild之上的相邻窗口,遮挡了pParent,减去pWin的窗口大小(加上边框) 34 // 如果pWin为compositor client管理,可能需要透明化,因此不能将其区域去掉 35 for (pWin = pParent->firstChild; pWin != pChild; pWin = pWin->nextSib) { 36 if (pWin->viewable && !TreatAsTransparent(pWin)) 37 RegionSubtract(&totalClip, &totalClip, &pWin->borderSize); 38 } 39 for (pWin = pChild; pWin; pWin = pWin->nextSib) 40 if (pWin->valdata && pWin->viewable) 41 viewvals++; 42 RegionEmpty(&pParent->clipList); 43 } 44 else { 45 // pChild后续相邻窗口的可见区域之和,即为pWin最大可见区域 46 if ((pChild->drawable.y < pParent->lastChild->drawable.y) || 47 ((pChild->drawable.y == pParent->lastChild->drawable.y) && 48 (pChild->drawable.x < pParent->lastChild->drawable.x))) { 49 forward = TRUE; 50 for (pWin = pChild; pWin; pWin = pWin->nextSib) { 51 if (pWin->valdata) { // 与移动窗口有重叠 52 RegionAppend(&totalClip, getBorderClip(pWin)); 53 if (pWin->viewable) 54 viewvals++; 55 } 56 } 57 } 58 else { 59 forward = FALSE; 60 pWin = pParent->lastChild; 61 while (1) { 62 if (pWin->valdata) { // 与移动窗口有重叠 63 RegionAppend(&totalClip, getBorderClip(pWin)); 64 if (pWin->viewable) 65 viewvals++; 66 } 67 if (pWin == pChild) 68 break; 69 pWin = pWin->prevSib; 70 } 71 } 72 RegionValidate(&totalClip, &overlap); 73 } 74 // Now go through the children of the root and figure their new borderClips from the totalClip, 75 // passing that off to miComputeClips to handle recursively. 76 // Once that's done, we remove the child from the totalClip to clip any siblings below it. 77 overlap = TRUE; 78 // 在可见区域(totalClip)内,计算子窗口间是否重叠,若无,则不需重绘 79 if (kind != VTStack) { 80 // 子窗口的可见区域加上父窗口的可见区域,为父窗口整个可见区域 81 RegionUnion(&totalClip, &totalClip, &pParent->clipList); 82 if (viewvals > 1) { 83 // precompute childUnion to discover whether any of them overlap. 84 // This seems redundant, but performance studies have demonstrated 85 // that the cost of this loop is lower than the cost of multiple Subtracts in the loop below. 86 RegionNull(&childUnion); 87 if (forward) { 88 for (pWin = pChild; pWin; pWin = pWin->nextSib) 89 if (pWin->valdata && pWin->viewable && !TreatAsTransparent(pWin)) 90 RegionAppend(&childUnion, &pWin->borderSize); 91 } 92 else { 93 pWin = pParent->lastChild; 94 while (1) { 95 if (pWin->valdata && pWin->viewable && !TreatAsTransparent(pWin)) 96 RegionAppend(&childUnion, &pWin->borderSize); 97 if (pWin == pChild) 98 break; 99 pWin = pWin->prevSib; 100 } 101 } 102 // 窗口之间是否有重叠,若无重叠,不需要重绘。 103 RegionValidate(&childUnion, &overlap); 104 if (overlap) 105 RegionUninit(&childUnion); 106 } 107 } 108 for (pWin = pChild; pWin != NullWindow; pWin = pWin->nextSib) { 109 if (pWin->viewable) { 110 if (pWin->valdata) { 111 RegionIntersect(&childClip, &totalClip, &pWin->borderSize); 112 // 计算pWin及其子窗口的clipList,boardList,exposed区域 113 miComputeClips(pWin, pScreen, &childClip, kind, &exposed); 114 if (overlap && !TreatAsTransparent(pWin)) { 115 // pWin遮挡了下层的窗口,下层窗口对boarderSize区域不可见,因此可以去掉该区域 116 RegionSubtract(&totalClip, &totalClip, &pWin->borderSize); 117 } 118 } 119 else if (pWin->visibility == VisibilityNotViewable) { 120 miTreeObscured(pWin); 121 } 122 } 123 else { // 不可显示,则清空clipList,删除临时数据 124 if (pWin->valdata) { 125 RegionEmpty(&pWin->clipList); 126 if (pScreen->ClipNotify) 127 (*pScreen->ClipNotify) (pWin, 0, 0); 128 RegionEmpty(&pWin->borderClip); 129 pWin->valdata = NULL; 130 } 131 } 132 } 133 RegionUninit(&childClip); 134 // 计算父窗口的可剪接区域,需重绘区域等 135 if (!overlap) { 136 // 没重叠,减去子窗口的可见区域,即为父窗口的可见区域 137 RegionSubtract(&totalClip, &totalClip, &childUnion); 138 RegionUninit(&childUnion); 139 } 140 RegionNull(&pParent->valdata->after.exposed); 141 RegionNull(&pParent->valdata->after.borderExposed); 142 143 // each case below is responsible for updating the clipList and serial number for the parent window 144 switch (kind) { 145 case VTStack: 146 // 子窗口间的堆叠层次变化,不影响父窗口的clipList区域 147 break; 148 default: 149 // totalClip contains the new clipList for the parent. 150 // Figure out exposures and obscures as per miComputeClips and reset the parent's clipList. 151 // 只重绘未绘制区域。clipList中的区域已绘制。边框不需要重绘,因为子窗口从未覆盖边框 152 RegionSubtract(&pParent->valdata->after.exposed, &totalClip, &pParent->clipList); 153 /* fall through */ 154 case VTMap: 155 // 更新clipList为新的可见区域 156 RegionCopy(&pParent->clipList, &totalClip); 157 pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER; 158 break; 159 } 160 161 RegionUninit(&totalClip); 162 RegionUninit(&exposed); 163 if (pScreen->ClipNotify) 164 (*pScreen->ClipNotify) (pParent, 0, 0); 165 return 1; 166}

HandleValidateExposures

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

  1. 重绘父窗口图形

    1. Server调用PaintWindow绘制边框

    2. 调用WindowExposures

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

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

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

1void miHandleValidateExposures(WindowPtr pWin){ 2 WindowPtr pChild; 3 ValidatePtr val; 4 WindowExposuresProcPtr WindowExposures; 5 pChild = pWin; 6 WindowExposures = pChild->drawable.pScreen->WindowExposures; 7 // 深度优先,先绘制边框,再绘制背景,然后发送Expose事件给客户端绘制 8 while (1) { 9 if ((val = pChild->valdata)) { 10 if (RegionNotEmpty(&val->after.borderExposed)) 11 // 调用miPaintWindow 12 pWin->drawable.pScreen->PaintWindow(pChild, &val->after.borderExposed, PW_BORDER); 13 RegionUninit(&val->after.borderExposed); 14 // xf86XVWindowExposures -> miWindowExposures 15 (*WindowExposures) (pChild, &val->after.exposed); 16 RegionUninit(&val->after.exposed); 17 free(val); 18 pChild->valdata = NULL; 19 if (pChild->firstChild) { 20 pChild = pChild->firstChild; 21 continue; 22 } 23 } 24 while (!pChild->nextSib && (pChild != pWin)) 25 pChild = pChild->parent; 26 if (pChild == pWin) 27 break; 28 pChild = pChild->nextSib; 29 } 30} 31 32void miWindowExposures(WindowPtr pWin, RegionPtr prgn){ 33 RegionPtr exposures = prgn; 34 if (prgn && !RegionNil(prgn)) { 35 RegionRec expRec; 36 int clientInterested = 37 (pWin->eventMask | wOtherEventMasks(pWin)) & ExposureMask; 38 if (clientInterested && (RegionNumRects(prgn) > RECTLIMIT)) { 39 // If we have LOTS of rectangles, we decide to take the extents and force an exposure on that. 40 // This should require much less work overall, on both client and server. 41 // This is cheating, but isn't prohibited by the protocol ("spontaneous combustion" :-). 42 BoxRec box = *RegionExtents(prgn); 43 exposures = &expRec; 44 RegionInit(exposures, &box, 1); 45 RegionReset(prgn, &box); 46 /* miPaintWindow doesn't clip, so we have to */ 47 RegionIntersect(prgn, prgn, &pWin->clipList); 48 } 49 // 重绘窗口背景 50 pWin->drawable.pScreen->PaintWindow(pWin, prgn, PW_BACKGROUND); 51 if (clientInterested) // 发送Expose事件 52 miSendExposures(pWin, exposures, pWin->drawable.x, pWin->drawable.y); 53 if (exposures == &expRec) 54 RegionUninit(exposures); 55 RegionEmpty(prgn); 56 } 57}


WindowsRestructured

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

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

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

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

1void WindowsRestructured(void){ 2 DeviceIntPtr pDev = inputInfo.devices; 3 while (pDev) { 4 if (IsMaster(pDev) || IsFloating(pDev)) // 主输入设备或者与主输入设备关联的设备 5 CheckMotion(NULL, pDev); 6 pDev = pDev->next; 7 } 8} 9 10Bool CheckMotion(DeviceEvent *ev, DeviceIntPtr pDev){ 11 WindowPtr prevSpriteWin, newSpriteWin; 12 SpritePtr pSprite = pDev->spriteInfo->sprite; 13 prevSpriteWin = pSprite->win; 14 // ev == NULL 15 if (ev && !syncEvents.playingEvents) { 16 // skipped 17 } 18 // 根据精灵的坐标获取包含在坐标的窗口 19 newSpriteWin = XYToWindow(pSprite, pSprite->hot.x, pSprite->hot.y); 20 if (newSpriteWin != prevSpriteWin) { // 光标所在的窗口发生了变化 21 int sourceid; 22 if (!ev) { 23 UpdateCurrentTimeIf(); 24 sourceid = pDev->id; /* when from WindowsRestructured */ 25 } 26 else 27 sourceid = ev->sourceid; 28 29 if (prevSpriteWin != NullWindow) { 30 // 焦点离开prevSpriteWin窗口,进入newSpriteWin窗口,发送事件给client 31 if (!ActivateEnterGrab(pDev, prevSpriteWin, newSpriteWin)) 32 DoEnterLeaveEvents(pDev, sourceid, prevSpriteWin, newSpriteWin, NotifyNormal); 33 } 34 pSprite->win = newSpriteWin; 35 // 重绘新光标 36 PostNewCursor(pDev); 37 return FALSE; 38 } 39 return TRUE; 40}

ConfigNotify

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

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

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

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

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

present_config_notify

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

1static int present_config_notify(WindowPtr window, int x, int y, int w, int h, int bw, WindowPtr sibling) { 2 int ret; 3 ScreenPtr screen = window->drawable.pScreen; 4 present_screen_priv_ptr screen_priv = present_screen_priv(screen); 5 // 发送PresentConfigureNotify扩展事件给客户端 6 present_send_config_notify(window, x, y, w, h, bw, sibling, 0); 7 unwrap(screen_priv, screen, ConfigNotify); 8 // 调用compConfigNotify 9 if (screen->ConfigNotify) 10 ret = screen->ConfigNotify (window, x, y, w, h, bw, sibling); 11 else 12 ret = 0; 13 wrap(screen_priv, screen, ConfigNotify, present_config_notify); 14 return ret; 15} 16

compConfigNotify

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

1 2int compConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw, WindowPtr pSib){ 3 ScreenPtr pScreen = pWin->drawable.pScreen; 4 CompScreenPtr cs = GetCompScreen(pScreen); 5 Bool ret = 0; 6 WindowPtr pParent = pWin->parent; 7 int draw_x, draw_y; 8 Bool alloc_ret; 9 // 调用DRI2ConfigNotify 10 if (cs->ConfigNotify) { 11 pScreen->ConfigNotify = cs->ConfigNotify; 12 ret = (*pScreen->ConfigNotify) (pWin, x, y, w, h, bw, pSib); 13 cs->ConfigNotify = pScreen->ConfigNotify; 14 pScreen->ConfigNotify = compConfigNotify; 15 if (ret) 16 return ret; 17 } 18 if (pWin->redirectDraw == RedirectDrawNone) 19 return Success; 20 // 计算窗口可绘制区域屏幕坐标,当尺寸不同时,重新分配pixmap 21 draw_x = pParent->drawable.x + x + bw; 22 draw_y = pParent->drawable.y + y + bw; 23 alloc_ret = compReallocPixmap(pWin, draw_x, draw_y, w, h, bw); 24 if (alloc_ret == FALSE) 25 return BadAlloc; 26 return Success; 27} 28 29Bool compReallocPixmap(WindowPtr pWin, int draw_x, int draw_y, unsigned int w, unsigned int h, int bw){ 30 ScreenPtr pScreen = pWin->drawable.pScreen; 31 PixmapPtr pOld = (*pScreen->GetWindowPixmap) (pWin); 32 PixmapPtr pNew; 33 CompWindowPtr cw = GetCompWindow(pWin); 34 int pix_x, pix_y; 35 int pix_w, pix_h; 36 cw->oldx = pOld->screen_x; 37 cw->oldy = pOld->screen_y; 38 pix_x = draw_x - bw; 39 pix_y = draw_y - bw; 40 pix_w = w + (bw << 1); 41 pix_h = h + (bw << 1); 42 if (pix_w != pOld->drawable.width || pix_h != pOld->drawable.height) { 43 // 创建一个新的pixmap,包括边框尺寸,从父窗口的画布的新区域中拷贝内容至新的pixmap中 44 pNew = compNewPixmap(pWin, pix_x, pix_y, pix_w, pix_h); 45 if (!pNew) 46 return FALSE; 47 // 缓冲旧的pixmap,设置新的pixmap给窗口 48 cw->pOldPixmap = pOld; 49 compSetPixmap(pWin, pNew, bw); 50 } 51 else { 52 pNew = pOld; // 尺寸不变(包括了边框),这更新坐标信息,旧坐标信息缓冲在cw->oldX/Y中 53 cw->pOldPixmap = 0; 54 } 55 pNew->screen_x = pix_x; // 包含的边框的屏幕坐标 56 pNew->screen_y = pix_y; 57 return TRUE; 58} 59

MoveWindow

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

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

  2. compMoveWindow:处理compWindow

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

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

xwl_move_window

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

1 2void xwl_move_window(WindowPtr window, int x, int y, WindowPtr next_sib, VTKind kind){ 3 ScreenPtr screen = window->drawable.pScreen; 4 struct xwl_screen *xwl_screen = xwl_screen_get(screen); 5 // 获取xwl_window窗口,window本身或者其祖先窗口对应的xwl_window窗口,存在xwl_window,则比存在一个wl_surface与window对应 6 struct xwl_window *xwl_window = xwl_window_from_window(window); 7 // 调用compMoveWindow 8 screen->MoveWindow = xwl_screen->MoveWindow; 9 (*screen->MoveWindow) (window, x, y, next_sib, kind); 10 xwl_screen->MoveWindow = screen->MoveWindow; 11 screen->MoveWindow = xwl_move_window; 12 // window本身有对应的xwl_window窗口,如:窗口管理器创建的装饰窗口 13 // 或window是顶层窗口(包括override-redirect和被窗口管理器装饰的顶层窗口) 14 if (xwl_window && (xwl_window_get(window) || xwl_window_is_toplevel(window))) 15 xwl_window_check_resolution_change_emulation(xwl_window); 16} 17 18// 处理窗口与视口(viewport)的关系 19void xwl_window_check_resolution_change_emulation(struct xwl_window *xwl_window) 20{ 21 struct xwl_emulated_mode emulated_mode; 22 struct xwl_output *xwl_output; 23 // Wayland compositor支持wp_viewporter_interface接口,获取一个xwl_output和对应的xwl_emulated_mode 24 if (xwl_window_should_enable_viewport(xwl_window, &xwl_output, &emulated_mode)) 25 xwl_window_enable_viewport(xwl_window, xwl_output, &emulated_mode); 26 else if (xwl_window_has_viewport_enabled(xwl_window)) 27 xwl_window_disable_viewport(xwl_window); 28} 29 30// 视口(viewport)与surface建立关系,并设置视口区域大小 31static void xwl_window_enable_viewport(struct xwl_window *xwl_window, 32 struct xwl_output *xwl_output, struct xwl_emulated_mode *emulated_mode){ 33 if (!xwl_window_has_viewport_enabled(xwl_window)) { 34 // 绑定视口与曲面(wl_surface) 35 xwl_window->viewport = wp_viewporter_get_viewport(xwl_window->xwl_screen->viewporter, xwl_window->surface); 36 } 37 // 设置窗口的坐标和尺寸 38 wp_viewport_set_source(xwl_window->viewport, wl_fixed_from_int(0), wl_fixed_from_int(0), 39 wl_fixed_from_int(emulated_mode->width), wl_fixed_from_int(emulated_mode->height)); 40 // 设置视口(viewport)的尺寸,可显示区域 41 wp_viewport_set_destination(xwl_window->viewport, xwl_output->width, xwl_output->height); 42 // 计算缩放比例 43 xwl_window->scale_x = (float)emulated_mode->width / xwl_output->width; 44 xwl_window->scale_y = (float)emulated_mode->height / xwl_output->height; 45}

compMoveWindow

回收compWindow中旧的pixmap资源

1void compMoveWindow(WindowPtr pWin, int x, int y, WindowPtr pSib, VTKind kind) { 2 ScreenPtr pScreen = pWin->drawable.pScreen; 3 CompScreenPtr cs = GetCompScreen(pScreen); 4 // 调用miMoveWindow 5 pScreen->MoveWindow = cs->MoveWindow; 6 (*pScreen->MoveWindow) (pWin, x, y, pSib, kind); 7 cs->MoveWindow = pScreen->MoveWindow; 8 pScreen->MoveWindow = compMoveWindow; 9 // 是否旧的pixmap,新pixmap可能在compCOnfigNofity中创建 10 compFreeOldPixmap(pWin); 11 compCheckTree(pScreen); 12} 13 14static void compFreeOldPixmap(WindowPtr pWin) { 15 ScreenPtr pScreen = pWin->drawable.pScreen; 16 if (pWin->redirectDraw != RedirectDrawNone) { 17 // 该窗口存在redirectWindow,且缓冲了旧的pixmap,则释放旧的pixmap 18 CompWindowPtr cw = GetCompWindow(pWin); 19 if (cw->pOldPixmap) { 20 (*pScreen->DestroyPixmap) (cw->pOldPixmap); 21 cw->pOldPixmap = NullPixmap; 22 } 23 } 24}

miMoveWindow

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

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

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

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

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

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

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

1void miMoveWindow(WindowPtr pWin, int x, int y, WindowPtr pNextSib, VTKind kind){ 2 WindowPtr pParent; 3 Bool WasViewable = (Bool) (pWin->viewable); 4 short bw; 5 RegionPtr oldRegion = NULL; 6 DDXPointRec oldpt; 7 Bool anyMarked = FALSE; 8 ScreenPtr pScreen; 9 WindowPtr windowToValidate; 10 WindowPtr pLayerWin; 11 if (!(pParent = pWin->parent)) 12 return; 13 pScreen = pWin->drawable.pScreen; 14 bw = wBorderWidth(pWin); 15 oldpt.x = pWin->drawable.x; 16 oldpt.y = pWin->drawable.y; 17 if (WasViewable) { 18 oldRegion = RegionCreate(NullBox, 1); 19 // 缓冲原剪接区间 20 RegionCopy(oldRegion, &pWin->borderClip); 21 // 调用miMarkOverlappedWindows,从pWin开始向后遍历其兄弟窗口,标记被pWin覆盖的窗口(窗口移动前),若存在返回true 22 anyMarked = (*pScreen->MarkOverlappedWindows) (pWin, pWin, &pLayerWin); 23 } 24 // origin (x,y)为窗口可绘制区域的新坐标,相对于父窗口 25 pWin->origin.x = x + (int) bw; 26 pWin->origin.y = y + (int) bw; 27 // (x,y)窗口可绘制区域的屏幕新坐标 28 x = pWin->drawable.x = pParent->drawable.x + x + (int) bw; 29 y = pWin->drawable.y = pParent->drawable.y + y + (int) bw; 30 // 设置窗口区域,若有compWindow,则为窗口实际大小,否则为父窗口范围内窗口区域 31 SetWinSize(pWin); 32 // 设置包含边框的窗口区域,原理同上 33 SetBorderSize(pWin); 34 // 回调PositionWindow通知X11扩展窗口位置更新 35 // miDbePositionWindow -> compPositionWindow -> fbPositionWindow 36 (*pScreen->PositionWindow) (pWin, x, y); 37 // 更新pWin在窗口栈的位置,并计算第一个在栈中重新暴露的窗口: 38 // pWin在pNextSib之上(窗口下移),则为pWin->nextSib 39 // pWin在pNextSib之下(窗口上移),则为pWin 40 windowToValidate = MoveWindowInStack(pWin, pNextSib); 41 // 重新计算子窗口在新位置的窗口尺寸,由于窗口的移动,可能导致某些内容移出父窗口,则必须截取窗口尺寸 42 ResizeChildrenWinSize(pWin, x - oldpt.x, y - oldpt.y, 0, 0); 43 if (WasViewable) { 44 if (pLayerWin == pWin) // 该条件恒成立,不知为什么需要判断 45 // 从windowToValidate开始向后遍历其兄弟窗口,标记与pWin重叠的窗口(窗口移动后(坐标已更新)) 46 anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, windowToValidate, NULL); 47 else 48 anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, pLayerWin, NULL); 49 if (anyMarked) { // 有重叠,有些窗口被遮挡,有些窗口重新显示 50 // 调用miValidateTree重新计算pWin父窗口及其子窗口的可见区域,并计算待重新绘制区域(需重新显示的区域) 51 (*pScreen->ValidateTree) (pLayerWin->parent, NullWindow, kind); 52 // compCopyWindow -> damageCopyWindow -> glamor_copy_window 53 // 将window中的内容拷贝至新位置 54 (*pWin->drawable.pScreen->CopyWindow) (pWin, oldpt, oldRegion); 55 RegionDestroy(oldRegion); 56 // 调用miHandleValidateExposures,深度优先,先绘制边框,再绘制背景,然后发送Expose事件给客户端绘制 57 (*pScreen->HandleExposures) (pLayerWin->parent); 58 if (pScreen->PostValidateTree) 59 (*pScreen->PostValidateTree) (pLayerWin->parent, NULL, kind); 60 } 61 } 62 if (pWin->realized) // 重构所有窗口 63 WindowsRestructured(); 64}

compCopyWindow

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

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

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

1 2void compCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) 3{ 4 ScreenPtr pScreen = pWin->drawable.pScreen; 5 CompScreenPtr cs = GetCompScreen(pScreen); 6 int dx = 0, dy = 0; 7 if (pWin->redirectDraw != RedirectDrawNone) { 8 PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin); 9 CompWindowPtr cw = GetCompWindow(pWin); 10 if (cw->pOldPixmap) { 11 // 窗口尺寸有改变,在compConfigNofity中创建了新的pixmap,旧的pixmap缓冲在pOldPixmap中 12 // 将pOldPixmap的内容拷贝至新的pixmap中即可,覆盖新pixmap中响应区域内容 13 RegionRec rgnDst; 14 GCPtr pGC; 15 // 计算窗口移动时产生的坐标偏移 16 dx = ptOldOrg.x - pWin->drawable.x; 17 dy = ptOldOrg.y - pWin->drawable.y; 18 RegionTranslate(prgnSrc, -dx, -dy); 19 RegionNull(&rgnDst); 20 // 区域限制在borderClip范围内 21 RegionIntersect(&rgnDst, &pWin->borderClip, prgnSrc); 22 // 坐标转换,从屏幕坐标转换为本地坐标(画布内的坐标) 23 RegionTranslate(&rgnDst, -pPixmap->screen_x, -pPixmap->screen_y); 24 // 坐标偏移转换为old和new pixmap内的偏移 25 dx = dx + pPixmap->screen_x - cw->oldx; 26 dy = dy + pPixmap->screen_y - cw->oldy; 27 pGC = GetScratchGC(pPixmap->drawable.depth, pScreen); 28 if (pGC) { 29 BoxPtr pBox = RegionRects(&rgnDst); 30 int nBox = RegionNumRects(&rgnDst); 31 ValidateGC(&pPixmap->drawable, pGC); 32 while (nBox--) { 33 // 调用glamor_copy_area拷贝数据 34 (void) (*pGC->ops->CopyArea) (&cw->pOldPixmap->drawable, &pPixmap->drawable, pGC, 35 pBox->x1 + dx, pBox->y1 + dy, pBox->x2 - pBox->x1, 36 pBox->y2 - pBox->y1, pBox->x1, pBox->y1); 37 pBox++; 38 } 39 FreeScratchGC(pGC); 40 } 41 RegionUninit(&rgnDst); 42 return; 43 } 44 // 矫正旧坐标,加上新pixmap的屏幕坐标与旧pixmap的偏移,方便后续操作区分 45 dx = pPixmap->screen_x - cw->oldx; 46 dy = pPixmap->screen_y - cw->oldy; 47 ptOldOrg.x += dx; 48 ptOldOrg.y += dy; 49 } 50 51 pScreen->CopyWindow = cs->CopyWindow; 52 if (ptOldOrg.x != pWin->drawable.x || ptOldOrg.y != pWin->drawable.y) { 53 // 窗口无compWindow,需要将内容从旧坐标移至新坐标 54 if (dx || dy) 55 RegionTranslate(prgnSrc, dx, dy); 56 // damageCopyWindow 57 (*pScreen->CopyWindow) (pWin, ptOldOrg, prgnSrc); 58 if (dx || dy) 59 RegionTranslate(prgnSrc, -dx, -dy); 60 } 61 else { 62 // 还原旧坐标,见上的矫正 63 ptOldOrg.x -= dx; 64 ptOldOrg.y -= dy; 65 // 更新prgnSrc至新坐标区域,构建Damage,通知新区域内容有变化 66 RegionTranslate(prgnSrc, pWin->drawable.x - ptOldOrg.x, pWin->drawable.y - ptOldOrg.y); 67 DamageDamageRegion(&pWin->drawable, prgnSrc); 68 } 69 cs->CopyWindow = pScreen->CopyWindow; 70 pScreen->CopyWindow = compCopyWindow; 71 compCheckTree(pWin->drawable.pScreen); 72}

damageCopyWindow

创建Damage,记录变化区域

1 2static void damageCopyWindow(WindowPtr pWindow, DDXPointRec ptOldOrg, RegionPtr prgnSrc){ 3 ScreenPtr pScreen = pWindow->drawable.pScreen; 4 damageScrPriv(pScreen); 5 if (getWindowDamage(pWindow)) { 6 int dx = pWindow->drawable.x - ptOldOrg.x; 7 int dy = pWindow->drawable.y - ptOldOrg.y; 8 // The region comes in source relative, but the damage occurs at the destination location. 9 // Translate back and forth. 10 RegionTranslate(prgnSrc, dx, dy); 11 damageRegionAppend(&pWindow->drawable, prgnSrc, FALSE, -1); 12 RegionTranslate(prgnSrc, -dx, -dy); 13 } 14 unwrap(pScrPriv, pScreen, CopyWindow); 15 // glamor_copy_window 16 (*pScreen->CopyWindow) (pWindow, ptOldOrg, prgnSrc); 17 damageRegionProcessPending(&pWindow->drawable); 18 wrap(pScrPriv, pScreen, CopyWindow, damageCopyWindow); 19}

glamor_copy_window

使用openGL拷贝数据

1 2void glamor_copy_window(WindowPtr window, DDXPointRec old_origin, RegionPtr src_region){ 3 PixmapPtr pixmap = glamor_get_drawable_pixmap(&window->drawable); 4 DrawablePtr drawable = &pixmap->drawable; 5 RegionRec dst_region; 6 int dx = old_origin.x - window->drawable.x; 7 int dy = old_origin.y - window->drawable.y; 8 RegionTranslate(src_region, -dx, -dy); 9 RegionNull(&dst_region); 10 RegionIntersect(&dst_region, &window->borderClip, src_region); 11 // pixmap的屏幕起始坐标记录在(screen_x, screen_y) 12 if (pixmap->screen_x || pixmap->screen_y) 13 RegionTranslate(&dst_region, -pixmap->screen_x, -pixmap->screen_y); 14 // 在同一个drawable中拷贝数据时,需考虑内容被覆盖的问题 15 // miCopyRegion考虑的拷贝方向(从上往下,从左往右),以避免内容被覆盖的问题 16 miCopyRegion(drawable, drawable, 0, &dst_region, dx, dy, glamor_copy, 0, 0); 17 RegionUninit(&dst_region); 18} 19 20// 采用opengl进行拷贝,通过reverse和upsidedown指定了拷贝数据时的方向 21// reverse:true为X轴上从右往左拷数据;false为X轴上从左往右拷数据 22// upsidedown:true为Y轴上从网上下拷数据;false为Y轴上从下往上拷数据 23void glamor_copy(DrawablePtr src, DrawablePtr dst, GCPtr gc, 24 BoxPtr box, int nbox, int dx, int dy, 25 Bool reverse, Bool upsidedown, Pixel bitplane, void *closure){ 26 if (nbox == 0) 27 return; 28 if (glamor_copy_gl(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure)) 29 return; 30 glamor_copy_bail(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure); 31}

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]

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

xwl_resize_window

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

1 2void xwl_resize_window(WindowPtr window, int x, int y, unsigned int width, unsigned int height, WindowPtr sib){ 3 ScreenPtr screen = window->drawable.pScreen; 4 struct xwl_screen *xwl_screen = xwl_screen_get(screen); 5 // 获取xwl_window窗口,window本身或者其祖先窗口对应的xwl_window窗口,存在xwl_window,则比存在一个wl_surface与window对应 6 struct xwl_window *xwl_window = xwl_window_from_window(window); 7 // 调用compResizeWindow 8 screen->ResizeWindow = xwl_screen->ResizeWindow; 9 (*screen->ResizeWindow) (window, x, y, width, height, sib); 10 xwl_screen->ResizeWindow = screen->ResizeWindow; 11 screen->ResizeWindow = xwl_resize_window; 12 // window本身有对应的xwl_window窗口,如:窗口管理器创建的装饰窗口 13 // 或window是顶层窗口(包括override-redirect和被窗口管理器装饰的顶层窗口) 14 if (xwl_window && (xwl_window_get(window) || xwl_window_is_toplevel(window))) 15 xwl_window_check_resolution_change_emulation(xwl_window); 16} 17 18// 处理窗口与视口(viewport)的关系 19void xwl_window_check_resolution_change_emulation(struct xwl_window *xwl_window){ 20 struct xwl_emulated_mode emulated_mode; 21 struct xwl_output *xwl_output; 22 // Wayland compositor支持wp_viewporter_interface接口,获取一个xwl_output和对应的xwl_emulated_mode 23 if (xwl_window_should_enable_viewport(xwl_window, &xwl_output, &emulated_mode)) 24 xwl_window_enable_viewport(xwl_window, xwl_output, &emulated_mode); 25 else if (xwl_window_has_viewport_enabled(xwl_window)) 26 // 找不到与窗口尺寸适配的xwl_emulated_mode,则需禁用原有的视口 27 xwl_window_disable_viewport(xwl_window); 28}

compResizeWindow

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

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

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发送重绘请求

1 2void miResizeWindow(WindowPtr pWin, int x, int y, unsigned int w, unsigned int h, WindowPtr pSib){ 3 WindowPtr pParent; 4 Bool WasViewable = (Bool) (pWin->viewable); 5 unsigned short width = pWin->drawable.width, height = pWin->drawable.height; 6 short oldx = pWin->drawable.x, oldy = pWin->drawable.y; 7 int bw = wBorderWidth(pWin); 8 short dw, dh; 9 DDXPointRec oldpt; 10 RegionPtr oldRegion = NULL; 11 Bool anyMarked = FALSE; 12 ScreenPtr pScreen; 13 WindowPtr pFirstChange; 14 WindowPtr pChild; 15 RegionPtr gravitate[StaticGravity + 1]; 16 unsigned g; 17 int nx, ny; /* destination x,y */ 18 int newx, newy; /* new inner window position */ 19 RegionPtr pRegion = NULL; 20 RegionPtr destClip; /* portions of destination already written */ 21 RegionPtr oldWinClip = NULL; /* old clip list for window */ 22 RegionPtr borderVisible = NullRegion; /* visible area of the border */ 23 Bool shrunk = FALSE; /* shrunk in an inner dimension */ 24 Bool moved = FALSE; /* window position changed */ 25 WindowPtr pLayerWin; 26 27 if (!(pParent = pWin->parent)) 28 return; 29 pScreen = pWin->drawable.pScreen; 30 newx = pParent->drawable.x + x + bw; 31 newy = pParent->drawable.y + y + bw; 32 if (WasViewable) { 33 anyMarked = FALSE; 34 oldRegion = RegionCreate(NullBox, 1); // save the visible region of the window 35 RegionCopy(oldRegion, &pWin->winSize); 36 // categorize child windows into regions to be moved 37 for (g = 0; g <= StaticGravity; g++) 38 gravitate[g] = (RegionPtr) NULL; 39 // 按照子窗口的winGravity分类,统计每类子窗口的borderClip集合,也即可显示区域(缩放前) 40 for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) { 41 g = pChild->winGravity; 42 if (g != UnmapGravity) { 43 if (!gravitate[g]) 44 gravitate[g] = RegionCreate(NullBox, 1); 45 RegionUnion(gravitate[g], gravitate[g], &pChild->borderClip); 46 } 47 else { 48 UnmapWindow(pChild, TRUE); 49 anyMarked = TRUE; 50 } 51 } 52 // 标记窗口缩放前被pWin遮挡的窗口 53 anyMarked |= (*pScreen->MarkOverlappedWindows) (pWin, pWin, &pLayerWin); 54 oldWinClip = NULL; 55 // bitGravity为ForgetGravity的窗口,每次缩放时需要重绘,因此无需缓冲其原可显示区域(clipList) 56 if (pWin->bitGravity != ForgetGravity) { 57 oldWinClip = RegionCreate(NullBox, 1); 58 RegionCopy(oldWinClip, &pWin->clipList); 59 } 60 //* if the window is changing size, borderExposed can't be computed correctly without some help. 61 if (pWin->drawable.height > h || pWin->drawable.width > w) 62 shrunk = TRUE; // 窗口在某个方向缩小 63 if (newx != oldx || newy != oldy) 64 moved = TRUE; // 伴随窗口移动 65 if ((pWin->drawable.height != h || pWin->drawable.width != w) && HasBorder(pWin)) { 66 // 存在缩放,且窗口有边框,计算边框可见区域 67 borderVisible = RegionCreate(NullBox, 1); 68 /* for tiled borders, we punt and draw the whole thing */ 69 if (pWin->borderIsPixel || !moved) { 70 if (shrunk || moved) // 缩小或者移动窗口,边框可能显示在原有的winSize内,因此需要去掉该区域 71 RegionSubtract(borderVisible, &pWin->borderClip, &pWin->winSize); 72 else // 边框肯定不在winSize区域内 73 RegionCopy(borderVisible, &pWin->borderClip); 74 } 75 } 76 } 77 // 更新窗口的origin和drawable,设置为新的坐标和尺寸 78 pWin->origin.x = x + bw; 79 pWin->origin.y = y + bw; 80 pWin->drawable.height = h; 81 pWin->drawable.width = w; 82 83 x = pWin->drawable.x = newx; 84 y = pWin->drawable.y = newy; 85 86 SetWinSize(pWin); 87 SetBorderSize(pWin); 88 // 更新子窗口的坐标和尺寸,若有窗口缩放,根据子窗口的winGravity计算新坐标 89 dw = (int) w - (int) width; 90 dh = (int) h - (int) height; 91 ResizeChildrenWinSize(pWin, x - oldx, y - oldy, dw, dh); 92 /* let the hardware adjust background and border pixmaps, if any */ 93 (*pScreen->PositionWindow) (pWin, x, y); 94 // 根据窗口栈信息,重排pWin,并返回pWin缩放引起的第一个显示变化的窗口 95 pFirstChange = MoveWindowInStack(pWin, pSib); 96 if (WasViewable) { 97 pRegion = RegionCreate(NullBox, 1); 98 if (pLayerWin == pWin) // 标记窗口缩放后与pWin重叠的窗口 99 anyMarked |= (*pScreen->MarkOverlappedWindows) (pWin, pFirstChange, NULL); 100 else 101 anyMarked |= (*pScreen->MarkOverlappedWindows) (pWin, pLayerWin, NULL); 102 if (pWin->valdata) { 103 pWin->valdata->before.resized = TRUE; 104 pWin->valdata->before.borderVisible = borderVisible; 105 } 106 if (anyMarked) 107 (*pScreen->ValidateTree) (pLayerWin->parent, pFirstChange, VTOther); 108 // the entire window is trashed unless bitGravity recovers portions of it 109 RegionCopy(&pWin->valdata->after.exposed, &pWin->clipList); 110 } 111 // 计算pWin窗口预留内容区域的新坐标(nx, ny) 112 GravityTranslate(x, y, oldx, oldy, dw, dh, pWin->bitGravity, &nx, &ny); 113 if (WasViewable) { 114 if (HasBorder(pWin)) { 115 int offx, offy, dx, dy; 116 // kruft to avoid double translates for each gravity 117 offx = 0; 118 offy = 0; 119 for (g = 0; g <= StaticGravity; g++) { 120 if (!gravitate[g]) 121 continue; 122 // align winSize to gravitate[g]. 123 // winSize is in new coordinates, gravitate[g] is still in old coordinates 124 // 相同gravity的子窗口大小集合不会超过winSize大小,因此winSize平移后 125 // 计算根据子窗口的gravity得到的缩放后的坐标(nx,ny) 126 GravityTranslate(x, y, oldx, oldy, dw, dh, g, &nx, &ny); 127 dx = (oldx - nx) - offx; 128 dy = (oldy - ny) - offy; 129 if (dx || dy) { 130 // winSize为窗口缩放后的区域,包含了相同gravity的子窗口区域集 131 // 平移winSize得到窗口缩放后子窗口在原坐标的窗口区域 132 RegionTranslate(&pWin->winSize, dx, dy); 133 offx += dx; 134 offy += dy; 135 } 136 // 取交集,计算得到可预留的子窗口内容区域(pWin窗口的缩放可能会导致子窗口内容丢失) 137 RegionIntersect(gravitate[g], gravitate[g], &pWin->winSize); 138 } 139 /* get winSize back where it belongs */ 140 if (offx || offy) 141 RegionTranslate(&pWin->winSize, -offx, -offy); 142 } 143 // add screen bits to the appropriate bucket 144 if (oldWinClip) { 145 // clip to new clipList 146 RegionCopy(pRegion, oldWinClip); 147 // 平移,计算pWin预留窗口内容的新区域。此处好像有bug,当窗口有边框时修改了(nx,ny),与无边框时值不同 148 RegionTranslate(pRegion, nx - oldx, ny - oldy); 149 // 取窗口缩放后的clipList的交集,得到pWin已绘制的可见区域 150 RegionIntersect(oldWinClip, pRegion, &pWin->clipList); 151 // don't step on any gravity bits which will be copied after this region. 152 // Note -- this assumes that the regions will be copied in gravity order. 153 for (g = pWin->bitGravity + 1; g <= StaticGravity; g++) { 154 if (gravitate[g]) // 去掉被原位置子窗口覆盖的区域,以减少计算量 155 RegionSubtract(oldWinClip, oldWinClip, gravitate[g]); 156 } 157 // 还原oldWinClip坐标 158 RegionTranslate(oldWinClip, oldx - nx, oldy - ny); 159 g = pWin->bitGravity; 160 if (!gravitate[g]) 161 gravitate[g] = oldWinClip; 162 else { 163 RegionUnion(gravitate[g], gravitate[g], oldWinClip); 164 RegionDestroy(oldWinClip); 165 } 166 } 167 // move the bits on the screen 168 destClip = NULL; 169 for (g = 0; g <= StaticGravity; g++) { 170 if (!gravitate[g]) 171 continue; 172 GravityTranslate(x, y, oldx, oldy, dw, dh, g, &nx, &ny); 173 oldpt.x = oldx + (x - nx); 174 oldpt.y = oldy + (y - ny); 175 // Note that gravitate[g] is *translated* by CopyWindow 176 // only copy the remaining useful bits 177 RegionIntersect(gravitate[g], gravitate[g], oldRegion); 178 // clip to not overwrite already copied areas 179 if (destClip) { 180 // destClip为新坐标区域,需平移至旧坐标区域进行操作 181 RegionTranslate(destClip, oldpt.x - x, oldpt.y - y); 182 RegionSubtract(gravitate[g], gravitate[g], destClip); 183 RegionTranslate(destClip, x - oldpt.x, y - oldpt.y); 184 } 185 // 窗口平移了,需较原窗口内容拷贝至新位置,不同winGravity的子窗口平移后相对位置有变化 186 if (oldpt.x != x || oldpt.y != y || pWin->redirectDraw) { 187 // 子窗口的预留内容拷贝至新区域,该函数还将gravitate[g]更新为新坐标区域 188 (*pWin->drawable.pScreen->CopyWindow) (pWin, oldpt, gravitate[g]); 189 } 190 // remove any overwritten bits from the remaining useful bits 191 RegionSubtract(oldRegion, oldRegion, gravitate[g]); 192 // recompute exposed regions of child windows 193 for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) { 194 if (pChild->winGravity != g) 195 continue; 196 // 计算子窗口需要重绘的区域(exposed) 197 RegionIntersect(pRegion, &pChild->borderClip, gravitate[g]); 198 TraverseTree(pChild, miRecomputeExposures, (void *) pRegion); 199 } 200 // remove the successfully copied regions of the window from its exposed region 201 if (g == pWin->bitGravity) 202 // pWin的部分区域已包含在gravitate[g]中,因此可从重绘区域中去掉 203 RegionSubtract(&pWin->valdata->after.exposed, &pWin->valdata->after.exposed, gravitate[g]); 204 if (!destClip) 205 destClip = gravitate[g]; 206 else { 207 RegionUnion(destClip, destClip, gravitate[g]); 208 RegionDestroy(gravitate[g]); 209 } 210 } 211 RegionDestroy(oldRegion); 212 RegionDestroy(pRegion); 213 if (destClip) 214 RegionDestroy(destClip); 215 if (anyMarked) { 216 (*pScreen->HandleExposures) (pLayerWin->parent); 217 if (pScreen->PostValidateTree) 218 (*pScreen->PostValidateTree) (pLayerWin->parent, pFirstChange, VTOther); 219 } 220 } 221 if (pWin->realized) 222 WindowsRestructured(); 223}

附录

ConfigureWindow

1int ConfigureWindow(WindowPtr pWin, Mask mask, XID *vlist, ClientPtr client){ 2 WindowPtr pSib = NullWindow; 3 WindowPtr pParent = pWin->parent; 4 Window sibwid = 0; 5 Mask index2, tmask; 6 XID *pVlist; 7 short x, y, beforeX, beforeY; 8 unsigned short w = pWin->drawable.width, 9 h = pWin->drawable.height, bw = pWin->borderWidth; 10 int rc, action, smode = Above; 11 // 输入窗口无边框 12 if ((pWin->drawable.class == InputOnly) && (mask & CWBorderWidth)) 13 return BadMatch; 14 // 指定了兄弟窗口,但未指定窗口栈掩码(调整窗口栈),改变窗口在窗口栈的顺序 15 if ((mask & CWSibling) && !(mask & CWStackMode)) 16 return BadMatch; 17 pVlist = vlist; 18 // 计算窗口坐标位置(x,y),该坐标为父窗口的相对坐标 19 if (pParent) { 20 x = pWin->drawable.x - pParent->drawable.x - (int) bw; 21 y = pWin->drawable.y - pParent->drawable.y - (int) bw; 22 } 23 else { 24 x = pWin->drawable.x; 25 y = pWin->drawable.y; 26 } 27 beforeX = x; 28 beforeY = y; 29 action = RESTACK_WIN; // 默认为调整栈顺序 30 if ((mask & (CWX | CWY)) && (!(mask & (CWHeight | CWWidth)))) { 31 GET_INT16(CWX, x); 32 GET_INT16(CWY, y); 33 action = MOVE_WIN; // 窗口坐标有变化,窗口尺寸不变 34 } 35 else if (mask & (CWX | CWY | CWWidth | CWHeight)) { 36 GET_INT16(CWX, x); 37 GET_INT16(CWY, y); 38 GET_CARD16(CWWidth, w); 39 GET_CARD16(CWHeight, h); 40 if (!w || !h) { 41 client->errorValue = 0; 42 return BadValue; 43 } 44 action = RESIZE_WIN; // 调整窗口大小,包括位置和尺寸 45 } 46 // 按照掩码获取请求参数,包括边框大小、兄弟窗口,窗口栈位置等 47 tmask = mask & ~ChangeMask; 48 while (tmask) { 49 index2 = (Mask) lowbit(tmask); 50 tmask &= ~index2; 51 switch (index2) { 52 case CWBorderWidth: 53 GET_CARD16(CWBorderWidth, bw); 54 break; 55 case CWSibling: 56 sibwid = (Window) *pVlist; 57 pVlist++; 58 rc = dixLookupWindow(&pSib, sibwid, client, DixGetAttrAccess); 59 if (rc != Success) { 60 client->errorValue = sibwid; 61 return rc; 62 } 63 if (pSib->parent != pParent) // 必须为兄弟窗口 64 return BadMatch; 65 if (pSib == pWin) 66 return BadMatch; 67 break; 68 case CWStackMode: 69 GET_CARD8(CWStackMode, smode); // 窗口在窗口栈中的位置 70 if ((smode != TopIf) && (smode != BottomIf) && 71 (smode != Opposite) && (smode != Above) && (smode != Below)) { 72 client->errorValue = smode; 73 return BadValue; 74 } 75 break; 76 default: 77 client->errorValue = mask; 78 return BadValue; 79 } 80 } 81 // root really can't be reconfigured, so just return 82 if (!pParent) 83 return Success; 84 // Figure out if the window should be moved. Doesn't make the changes to the window if event sent. 85 if (mask & CWStackMode) // 计算pWin窗口在窗口栈的位置,窗口放置在返回的pSib窗口之上 86 pSib = WhereDoIGoInTheStack(pWin, pSib, pParent->drawable.x + x, pParent->drawable.y + y, 87 w + (bw << 1), h + (bw << 1), smode); 88 else 89 pSib = pWin->nextSib; 90 // 重定向ConfigureWindow请求,如发送给窗口管理器处理等(发送ConfigureRequest事件) 91 if ((!pWin->overrideRedirect) && (RedirectSend(pParent))) { 92 xEvent event = { 93 .u.configureRequest.window = pWin->drawable.id, 94 .u.configureRequest.sibling = (mask & CWSibling) ? sibwid : None, 95 .u.configureRequest.x = x, 96 .u.configureRequest.y = y, 97 .u.configureRequest.width = w, 98 .u.configureRequest.height = h, 99 .u.configureRequest.borderWidth = bw, 100 .u.configureRequest.valueMask = mask, 101 .u.configureRequest.parent = pParent->drawable.id 102 }; 103 event.u.u.type = ConfigureRequest; 104 event.u.u.detail = (mask & CWStackMode) ? smode : Above; 105 if (MaybeDeliverEventsToClient(pParent, &event, 1, SubstructureRedirectMask, client) == 1) 106 return Success; 107 } 108 if (action == RESIZE_WIN) { 109 Bool size_change = (w != pWin->drawable.width) || (h != pWin->drawable.height); 110 if (size_change && ((pWin->eventMask | wOtherEventMasks(pWin)) & ResizeRedirectMask)) { 111 // 重定向Resize Window,发送ResizeRequest给感兴趣的client 112 xEvent eventT = { 113 .u.resizeRequest.window = pWin->drawable.id, 114 .u.resizeRequest.width = w, 115 .u.resizeRequest.height = h 116 }; 117 eventT.u.u.type = ResizeRequest; 118 if (MaybeDeliverEventsToClient(pWin, &eventT, 1, ResizeRedirectMask, client) == 1) { 119 /* if event is delivered, leave the actual size alone. */ 120 w = pWin->drawable.width; 121 h = pWin->drawable.height; 122 size_change = FALSE; 123 } 124 } 125 if (!size_change) { 126 if (mask & (CWX | CWY)) 127 action = MOVE_WIN; 128 else if (mask & (CWStackMode | CWBorderWidth)) 129 action = RESTACK_WIN; 130 else /* really nothing to do */ 131 return (Success); 132 } 133 } 134 135 if (action == RESIZE_WIN) 136 goto ActuallyDoSomething; 137 if ((mask & CWX) && (x != beforeX)) 138 goto ActuallyDoSomething; 139 if ((mask & CWY) && (y != beforeY)) 140 goto ActuallyDoSomething; 141 if ((mask & CWBorderWidth) && (bw != wBorderWidth(pWin))) 142 goto ActuallyDoSomething; 143 if (mask & CWStackMode) { 144 if (pWin->nextSib != pSib) // 相同,则窗口栈顺序不变 145 goto ActuallyDoSomething; 146 } 147 return Success; 148 149 ActuallyDoSomething: 150 if (pWin->drawable.pScreen->ConfigNotify) { 151 // 通过回调ConfigNotify函数告知扩展协议窗口配置变化 152 int ret = (*pWin->drawable.pScreen->ConfigNotify) (pWin, x, y, w, h, bw, pSib); 153 if (ret) { 154 client->errorValue = 0; 155 return ret; 156 } 157 } 158 if (SubStrSend(pWin, pParent)) { 159 // 发送ConfigureNotify事件,通知感兴趣的client窗口配置变化 160 xEvent event = { 161 .u.configureNotify.window = pWin->drawable.id, 162 .u.configureNotify.aboveSibling = pSib ? pSib->drawable.id : None, 163 .u.configureNotify.x = x, 164 .u.configureNotify.y = y, 165 .u.configureNotify.width = w, 166 .u.configureNotify.height = h, 167 .u.configureNotify.borderWidth = bw, 168 .u.configureNotify.override = pWin->overrideRedirect 169 }; 170 event.u.u.type = ConfigureNotify; 171 DeliverEvents(pWin, &event, 1, NullWindow); 172 } 173 if (mask & CWBorderWidth) { 174 if (action == RESTACK_WIN) { 175 action = MOVE_WIN; 176 pWin->borderWidth = bw; 177 } 178 else if ((action == MOVE_WIN) && 179 (beforeX + wBorderWidth(pWin) == x + (int) bw) && 180 (beforeY + wBorderWidth(pWin) == y + (int) bw)) { 181 // 窗口边框大小引起的窗口坐标变化,即窗口可绘制区域未变化,且位置不变 182 action = REBORDER_WIN; 183 (*pWin->drawable.pScreen->ChangeBorderWidth) (pWin, bw); 184 } 185 else 186 pWin->borderWidth = bw; 187 } 188 if (action == MOVE_WIN) 189 (*pWin->drawable.pScreen->MoveWindow) (pWin, x, y, pSib, 190 (mask & CWBorderWidth) ? VTOther : VTMove); 191 else if (action == RESIZE_WIN) 192 (*pWin->drawable.pScreen->ResizeWindow) (pWin, x, y, w, h, pSib); 193 else if (mask & CWStackMode) 194 ReflectStackChange(pWin, pSib, VTOther); 195 if (action != RESTACK_WIN) 196 CheckCursorConfinement(pWin); 197 return Success; 198}

miComputeClips

1static void miComputeClips(WindowPtr pParent, ScreenPtr pScreen, RegionPtr universe, VTKind kind, RegionPtr exposed) { 2 int dx, dy; 3 RegionRec childUniverse; 4 WindowPtr pChild; 5 int oldVis, newVis; 6 BoxRec borderSize; 7 RegionRec childUnion; 8 Bool overlap; 9 RegionPtr borderVisible; 10 11 // Figure out the new visibility of this window. 12 // The extent of the universe should be the same as the extent of the borderSize region. 13 // If the window is unobscured, this rectangle will be completely inside the universe 14 // (the universe will cover it completely). 15 // If the window is completely obscured, none of the universe will cover the rectangle. 16 borderSize.x1 = pParent->drawable.x - wBorderWidth(pParent); 17 borderSize.y1 = pParent->drawable.y - wBorderWidth(pParent); 18 // 坐标要比32K小(16bits) 19 dx = (int) pParent->drawable.x + (int) pParent->drawable.width + wBorderWidth(pParent); 20 if (dx > 32767) dx = 32767; 21 borderSize.x2 = dx; 22 dy = (int) pParent->drawable.y + (int) pParent->drawable.height + wBorderWidth(pParent); 23 if (dy > 32767) dy = 32767; 24 borderSize.y2 = dy; 25 26 // In redirected drawing case, reset universe to borderSize 27 if (pParent->redirectDraw != RedirectDrawNone) { 28 if (TreatAsTransparent(pParent)) 29 RegionEmpty(universe); 30 compSetRedirectBorderClip (pParent, universe); 31 RegionCopy(universe, &pParent->borderSize); 32 } 33 oldVis = pParent->visibility; 34 // 计算窗口的可见性,全部可见,部分可见,全不可见。borderSize被universe全包含,则全部可见 35 switch (RegionContainsRect(universe, &borderSize)) { 36 case rgnIN: 37 newVis = VisibilityUnobscured; 38 break; 39 case rgnPART: 40 newVis = VisibilityPartiallyObscured; 41 { 42 RegionPtr pBounding; 43 // shape扩展根据指定的shape边界调整窗口可见性 44 if ((pBounding = wBoundingShape(pParent))) { 45 switch (miShapedWindowIn(universe, pBounding, &borderSize, pParent->drawable.x, pParent->drawable.y)) { 46 case rgnIN: 47 newVis = VisibilityUnobscured; 48 break; 49 case rgnOUT: 50 newVis = VisibilityFullyObscured; 51 break; 52 } 53 } 54 } 55 break; 56 default: 57 newVis = VisibilityFullyObscured; 58 break; 59 } 60 pParent->visibility = newVis; 61 // 可见性有改变,且client关心该窗口可见性变化,则发送VisibilityNotify事件 62 if (oldVis != newVis && ((pParent->eventMask | wOtherEventMasks(pParent)) & VisibilityChangeMask)) 63 SendVisibilityNotify(pParent); 64 // 窗口在计算重叠窗口后,可能会有所调整。dx,dy表示起始坐标调整幅度 65 dx = pParent->drawable.x - pParent->valdata->before.oldAbsCorner.x; 66 dy = pParent->drawable.y - pParent->valdata->before.oldAbsCorner.y; 67 68 // avoid computations when dealing with simple operations 69 switch (kind) { 70 case VTMap: 71 case VTStack: 72 case VTUnmap: 73 break; 74 case VTMove: 75 if ((oldVis == newVis) && ((oldVis == VisibilityFullyObscured) || (oldVis == VisibilityUnobscured))) { 76 pChild = pParent; 77 // 深度优先,处理pParent所有子窗口,子窗口需随父窗口移动,无需重绘内容(除了边框) 78 while (1) { 79 if (pChild->viewable) { 80 if (pChild->visibility != VisibilityFullyObscured) { 81 // 窗口移动,更新borderClip和clipList区域坐标 82 RegionTranslate(&pChild->borderClip, dx, dy); 83 RegionTranslate(&pChild->clipList, dx, dy); 84 pChild->drawable.serialNumber = NEXT_SERIAL_NUMBER; 85 // ClipNotify函数回调:compClipNotify -> xf86XVClipNotify 86 if (pScreen->ClipNotify) 87 (*pScreen->ClipNotify) (pChild, dx, dy); 88 } 89 if (pChild->valdata) { 90 RegionNull(&pChild->valdata->after.borderExposed); 91 if (HasParentRelativeBorder(pChild)) { 92 // 有边框,则记录边框暴露区域(borderClip - winSize),4个边框区域 93 RegionSubtract(&pChild->valdata->after.borderExposed, &pChild->borderClip, &pChild->winSize); 94 } 95 // 因可见性没变,不需要重绘 96 RegionNull(&pChild->valdata->after.exposed); 97 } 98 if (pChild->firstChild) { 99 pChild = pChild->firstChild; 100 continue; 101 } 102 } 103 while (!pChild->nextSib && (pChild != pParent)) 104 pChild = pChild->parent; 105 if (pChild == pParent) 106 break; 107 pChild = pChild->nextSib; 108 } 109 return; 110 } 111 /* fall through */ 112 default: 113 // To calculate exposures correctly, we have to translate the old borderClip and clipList regions 114 // to the window's new location so there is a correspondence between pieces of the new and old clipping regions. 115 if (dx || dy) { 116 // We translate the old clipList because that will be exposed or copied if gravity is right. 117 RegionTranslate(&pParent->borderClip, dx, dy); 118 RegionTranslate(&pParent->clipList, dx, dy); 119 } 120 break; 121 case VTBroken: 122 RegionEmpty(&pParent->borderClip); 123 RegionEmpty(&pParent->clipList); 124 break; 125 } 126 127 borderVisible = pParent->valdata->before.borderVisible; 128 RegionNull(&pParent->valdata->after.borderExposed); 129 RegionNull(&pParent->valdata->after.exposed); 130 131 // Since the borderClip must not be clipped by the children, we do the border exposure first... 132 // 'universe' is the window's borderClip. 133 // To figure the exposures, remove the area that used to be exposed from the new. 134 // This leaves a region of pieces that weren't exposed before. 135 if (HasBorder(pParent)) { 136 if (borderVisible) { 137 // when the border changes shape, the old visible portions of the border will be saved by DIX in borderVisible -- 138 // use that region and destroy it 139 // 包含边框的区域已显示,去掉该区域 140 RegionSubtract(exposed, universe, borderVisible); 141 RegionDestroy(borderVisible); 142 } 143 else { 144 // 暴露区域为去掉已绘制的包含边框的区域 145 RegionSubtract(exposed, universe, &pParent->borderClip); 146 } 147 if (HasParentRelativeBorder(pParent) && (dx || dy)) 148 // 边框需要重绘,(窗口移动且backgroundState == ParentRelative) 149 RegionSubtract(&pParent->valdata->after.borderExposed, universe, &pParent->winSize); 150 else 151 // 在可暴露区域范围内重绘边框,去掉可绘制窗口区域 152 RegionSubtract(&pParent->valdata->after.borderExposed, exposed, &pParent->winSize); 153 // 缓冲包含边框的可绘制区域 154 RegionCopy(&pParent->borderClip, universe); 155 156 // To get the right clipList for the parent, and to make doubly sure that no child overlaps the parent's border, 157 // we remove the parent's border from the universe before proceeding. 158 // 可绘制窗口区域 159 RegionIntersect(universe, universe, &pParent->winSize); 160 } 161 else 162 RegionCopy(&pParent->borderClip, universe); 163 164 if ((pChild = pParent->firstChild) && pParent->mapped) { 165 // 获取子窗口覆盖的区域childUnion,包括边框 166 RegionNull(&childUniverse); 167 RegionNull(&childUnion); 168 if ((pChild->drawable.y < pParent->lastChild->drawable.y) || 169 ((pChild->drawable.y == pParent->lastChild->drawable.y) && 170 (pChild->drawable.x < pParent->lastChild->drawable.x))) { 171 for (; pChild; pChild = pChild->nextSib) { 172 if (pChild->viewable && !TreatAsTransparent(pChild)) 173 RegionAppend(&childUnion, &pChild->borderSize); 174 } 175 } 176 else { 177 for (pChild = pParent->lastChild; pChild; pChild = pChild->prevSib) { 178 if (pChild->viewable && !TreatAsTransparent(pChild)) 179 RegionAppend(&childUnion, &pChild->borderSize); 180 } 181 } 182 RegionValidate(&childUnion, &overlap); 183 // 按照深度优先递归处理子窗口的可剪接区域 184 for (pChild = pParent->firstChild; pChild; pChild = pChild->nextSib) { 185 if (pChild->viewable) { 186 // If the child is viewable, we want to remove its extents from the current universe, 187 // but we only re-clip it if it's been marked. 188 if (pChild->valdata) { 189 // Figure out the new universe from the child's perspective and recurse. 190 // 子窗口可绘制区域。所有子窗口可绘制区域universe与窗口大小的交集 191 RegionIntersect(&childUniverse, universe, &pChild->borderSize); 192 miComputeClips(pChild, pScreen, &childUniverse, kind, exposed); 193 } 194 // Once the child has been processed, 195 // we remove its extents from the current universe, thus denying its space to any other sibling. 196 if (overlap && !TreatAsTransparent(pChild)) 197 // 有子窗口间有重叠,减去pChild窗口大小 198 // 窗口树堆叠顺序,确保后处理的子窗口被前处理的子窗口覆盖,因此可视区域可直接减去窗口区域 199 RegionSubtract(universe, universe, &pChild->borderSize); 200 } 201 } 202 // 子窗口无重叠,减去所有子窗口的区域,即为父窗口可见区域。universe为最终父窗口可见区域 203 if (!overlap) 204 RegionSubtract(universe, universe, &childUnion); 205 RegionUninit(&childUnion); 206 RegionUninit(&childUniverse); 207 } /* if any children */ 208 209 // 'universe' now contains the new clipList for the parent window. 210 // To figure the exposure of the window we subtract the old clip from the new, just as for the border. 211 if (oldVis == VisibilityFullyObscured || oldVis == VisibilityNotViewable) { 212 // 之前被遮挡,现在被暴露,需重绘 213 RegionCopy(&pParent->valdata->after.exposed, universe); 214 } 215 else if (newVis != VisibilityFullyObscured && newVis != VisibilityNotViewable) { 216 // 之前有显示区域(clipList),现在只需重绘未显示的可见区域 217 RegionSubtract(&pParent->valdata->after.exposed, universe, &pParent->clipList); 218 } 219 220 /* HACK ALERT - copying contents of regions, instead of regions */ 221 { 222 RegionRec tmp; 223 tmp = pParent->clipList; 224 // 更新父窗口的可见区域,这些区域已或者将被重绘 225 pParent->clipList = *universe; 226 // 更改为原有的可剪接区域(clipList) 227 *universe = tmp; 228 } 229 230 pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER; 231 // 调用ClipNotify回调,坐标偏移为(dx, dy) 232 if (pScreen->ClipNotify) 233 (*pScreen->ClipNotify) (pParent, dx, dy); 234}

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重排在栈底层

1// 返回新栈中pWin窗口下的第一个兄弟窗口 2// 返回NULL,表示pWin重排在栈低 3// 返回pWin->nextSib,表示栈不变 4static WindowPtr WhereDoIGoInTheStack(WindowPtr pWin, WindowPtr pSib, 5 short x, short y, unsigned short w, unsigned short h, int smode){ 6 BoxRec box; 7 WindowPtr pHead, pFirst; 8 9 if ((pWin == pWin->parent->firstChild) && (pWin == pWin->parent->lastChild)) 10 return NULL; 11 pHead = RealChildHead(pWin->parent); 12 pFirst = pHead ? pHead->nextSib : pWin->parent->firstChild; 13 box.x1 = x; 14 box.y1 = y; 15 box.x2 = x + (int) w; 16 box.y2 = y + (int) h; 17 switch (smode) { 18 case Above: 19 if (pSib) 20 return pSib; 21 else if (pWin == pFirst) 22 return pWin->nextSib; 23 else 24 return pFirst; 25 case Below: 26 if (pSib) 27 if (pSib->nextSib != pWin) 28 return pSib->nextSib; 29 else 30 return pWin->nextSib; 31 else 32 return NullWindow; 33 case TopIf: 34 if ((!pWin->mapped || (pSib && !pSib->mapped))) 35 return pWin->nextSib; 36 else if (pSib) { 37 if ((IsSiblingAboveMe(pWin, pSib) == Above) && 38 (RegionContainsRect(&pSib->borderSize, &box) != rgnOUT)) 39 return pFirst; 40 else 41 return pWin->nextSib; 42 } 43 else if (AnyWindowOverlapsMe(pWin, pHead, &box)) 44 return pFirst; 45 else 46 return pWin->nextSib; 47 case BottomIf: 48 if ((!pWin->mapped || (pSib && !pSib->mapped))) 49 return pWin->nextSib; 50 else if (pSib) { 51 if ((IsSiblingAboveMe(pWin, pSib) == Below) && 52 (RegionContainsRect(&pSib->borderSize, &box) != rgnOUT)) 53 return NullWindow; 54 else 55 return pWin->nextSib; 56 } 57 else if (IOverlapAnyWindow(pWin, &box)) 58 return NullWindow; 59 else 60 return pWin->nextSib; 61 case Opposite: 62 if ((!pWin->mapped || (pSib && !pSib->mapped))) 63 return pWin->nextSib; 64 else if (pSib) { 65 if (RegionContainsRect(&pSib->borderSize, &box) != rgnOUT) { 66 if (IsSiblingAboveMe(pWin, pSib) == Above) 67 return pFirst; 68 else 69 return NullWindow; 70 } 71 else 72 return pWin->nextSib; 73 } 74 else if (AnyWindowOverlapsMe(pWin, pHead, &box)) { 75 /* If I'm occluded, I can't possibly be the first child 76 * if (pWin == pWin->parent->firstChild) 77 * return pWin->nextSib; 78 */ 79 return pFirst; 80 } 81 else if (IOverlapAnyWindow(pWin, &box)) 82 return NullWindow; 83 else 84 return pWin->nextSib; 85 default: 86 return pWin->nextSib; // should never happen; make something up. 87 } 88}