1.3.5 Configure Window
- 1 ReflectStackChange
- 2 ConfigNotify
- 3 MoveWindow
- 3.1 xwl_move_window
- 3.2 compMoveWindow
- 3.3 miMoveWindow
- 3.3.1 compCopyWindow
- 3.3.2 damageCopyWindow
- 3.3.3 glamor_copy_window
- 4 ResizeWindow
- 4.1 xwl_resize_window
- 4.2 compResizeWindow
- 4.3 miResizeWindow
- 5 附录
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.
}
}