client通过发送ConfigureWindow请求配置窗口,配置窗口的内容包括窗口位置、窗口尺寸、窗口栈的调整、窗口边框大小等。X11 Server通过ProcConfigureWindow请求,调用ConfigureWindow实现。
根据掩码获取窗口的新坐标,宽高尺寸(CWX | CWY | CWWidth | CWHeight)等。涉及窗口移动和缩放
根据掩码获取窗口边框大小,窗口栈调整位置等
CWBorderWidth:调整窗口边框
CWStackMode:调整窗口在栈中的位置,包括:TopIf、BottomIf、Opposite、Above、Below等;其中CWSibling可指定相对哪个兄弟窗口的栈位置变化
重定向ConfigureWindow请求至管理客户端,如:窗口管理器等,发送ConfigureRequest事件
重定向窗口缩放至管理客户端,发送ResizeRequest事件
通过回调ConfigNotify函数告知服务端的X11扩展协议窗口配置变化,如:composite等
通过发送ConfigureNotify事件,通知感兴趣的client窗口配置变化
回调ChangeBorderWidth处理边框
回调MoveWindow处理窗口移动
回调ResizeWindow处理窗口缩放
调用ReflectStackChange处理窗口在窗口栈中的位置变化
调用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区域。
计算可能变化的可见区域(totalClip),为减少重绘开销。有两种方式:
父窗口的borderClip区域减去pChild之上的子窗口区域(boarderSize),这些子窗口区域的显示内容不会改变
所有与移动窗口有重叠的子窗口的可见区域之和,为父窗口的最大可见区域,这些区域的内容会发生变化
过滤优化:在可见区域(totalClip)内,计算子窗口间是否重叠,若无,则不需重绘
子窗口的clipList,boardList,exposed区域,按照深度优先递归计算窗口的可见区域
窗口当可见性发生变化时(Unobscured, PartiallyObscured, FullyObscured),可能发送VisibilityNotify事件
当可见区域发生变化时,回调ClipNotify函数(present_clip_notify/compClipNotify/xf86XVClipNotify)
计算父窗口的可见区域,需重绘区域(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)事件,采用深度优先遍历窗口树
重绘父窗口图形
Server调用PaintWindow绘制边框
调用WindowExposures
Server调用PaintWindow绘制窗口背景
发送重绘事件给客户端
重绘子窗口图形,流程与上类似
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
处理光标和键盘焦点,并重构光标窗口:
计算获得当前焦点坐标所在窗口(最低级窗口)
若当前焦点窗口与原窗口不同,则将焦点离开原窗口,进入新窗口
如果是光标设备,则重绘光标窗口,根据光标所在窗口特性,可绘制不同的光标图形,一般由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扩展窗口配置发送变化,涉及一个回调函数链,其调用顺序为:
present_config_notify:发送PresentConfigureNotify扩展事件给客户端
compConfigNotify:针对拥有compWindow的窗口进行处理,若窗口尺寸有变化(ResizeWindow),则分配新的pixmap
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的回调函数。其调用顺序为:
xwl_move_window:更新顶层窗口的视口(viewport)
compMoveWindow:处理compWindow
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
实现窗口移动功能,窗口移动涉及原被遮挡窗口内容显示,窗口新位置内容显示,窗口栈的更新等,同时其子窗口也随之移动。
标记在父窗口树中被pWin覆盖的窗口和子窗口,包括pWin本身以及子窗口
更新pWin窗口尺寸和坐标,坐标平移,截取超过父窗口边界的区域
更新pWin子窗口的尺寸和坐标,坐标平移,截取超过pWin窗口边界的区域
根据pWin的新窗口,计算屏幕可能变化的区域,并计算窗口及子窗口的可见区域,重绘区域等。
将pWin窗口的原有效内容迁移至新窗口内
根据窗口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
分两种情况更新新区域内容
窗口有compWindow,若窗口尺寸未变,则创建Damage,告知新区域内容有变化;若窗口尺寸变化时,pixmap为新区域的内容,需将旧pixmap的内容拷贝至pixmap中,更新窗口内容至新区域。
窗口无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设置)。实现思路为:
统计不同winGravity子窗口在原窗口中的可见区域
标记在父窗口树中被pWin覆盖的窗口和子窗口,包括pWin本身以及子窗口
更新pWin窗口尺寸和坐标,变为新窗口区域
更新pWin子窗口的尺寸和坐标,子窗口的坐标变化受其winGravity影响(而非平移),尺寸截取在pWin新窗口内。子窗口的子窗口不受winGravity影响,与其父窗口保存相对位置
根据pWin的新窗口,计算屏幕可能变化的区域,并计算窗口及子窗口的可见区域,重绘区域等。
根据pWin的bitGravity计算窗口预留内容在新窗口的起始位置(nx, ny)
计算原窗口已绘制区域在新窗口有效的区域。新窗口的有效可见区域与原窗口已绘制区域在新窗口的重叠区。并将该区域归至相同gravity的子窗口区域中
将子窗口原内容迁移至新窗口内
确定子窗口的有效区域:计算Gravity分组的原子窗口区域与其在新窗口的重叠区域,该区域为子窗口有效区域
将子窗口有效区域的内容移至新窗口中
计算子窗口其它可见区域,用于通知client重绘
根据窗口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 评论