ReactOS 图形系统分析(15):窗口对象 — EWNDOBJ(engwindow.c)

ReactOS 图形系统分析(15):窗口对象 --- EWNDOBJ(engwindow.c)

1. 概述

engwindow.c 位于 win32ss/gdi/eng/engwindow.c,是 GDI 引擎的窗口对象(WNDOBJ/EWNDOBJ)管理模块。它让显示驱动能够跟踪窗口的位置与可见区域变化,从而优化窗口表面的绘制(如硬件加速、合成器)。

复制代码
ntuser (WND 窗口对象:位置/尺寸/可见区域)
    ↓ VIS_ComputeVisibleRegion 计算可见区域
engwindow.c (EWNDOBJ 管理 ← 本分析对象)
    ├── EngCreateWnd / EngDeleteWnd    生命周期
    ├── IntEngWndUpdateClipObj         从 WND 更新裁剪对象
    ├── IntEngWindowChanged            窗口变化通知(回调驱动)
    └── WNDOBJ_bEnum / cEnumStart      可见区域矩形枚举
    ↓
显示驱动 (通过 WNDOBJCHANGEPROC 接收变化通知)

核心思想: 驱动创建窗口表面时调用 EngCreateWnd 获得 WNDOBJ,之后窗口移动/改变大小时引擎通过 WNDOBJCHANGEPROC 回调通知驱动,驱动可据此高效更新(例如只重绘露出部分,或跟踪精灵位置)。


2. 核心数据结构

2.1 EWNDOBJ(扩展窗口对象,engobjects.h(file:///d:/reactos/win32ss/gdi/eng/engobjects.h#L92-L105))

c 复制代码
typedef struct _EWNDOBJ : _XCLIPOBJ      // 继承:CLIPOBJ + RWNDOBJ + 裁剪私有字段
{
    /* 扩展窗口对象部分 */
    HWND              Hwnd;              // 关联的窗口句柄
    WNDOBJCHANGEPROC  ChangeProc;        // 变化回调(驱动注册)
    FLONG             Flags;             // 关心的变化标志
    int               PixelFormat;       // 像素格式(OpenGL 用)
} EWNDOBJ, *PEWNDOBJ;

2.2 WNDOBJ(DDI 公开部分,= CLIPOBJ + RWNDOBJ)

c 复制代码
typedef struct _RWNDOBJ {
  PVOID   pvConsumer;      // 驱动私有数据
  RECTL   rclClient;       // 客户区矩形
  SURFOBJ *psoOwner;       // 所有者表面
} RWNDOBJ;

/* WNDOBJ = CLIPOBJ + RWNDOBJ(通过 _WNDOBJ 别名引用) */

WNDOBJ 本质是一个特殊的 CLIPOBJ :它的裁剪区域 = 窗口的可见客户区。驱动通过 WNDOBJ_bEnum/WNDOBJ_cEnumStart 枚举可见矩形(复用 CLIPOBJ 机制,见《ReactOS 图形系统分析_8.md》)。

2.3 变化标志

驱动请求的标志(WO_*,EngCreateWnd 的 fl 参数):

标志 含义
WO_RGN_CLIENT 客户区变化通知
WO_RGN_CLIENT_DELTA 客户区增量变化
WO_RGN_SURFACE 表面区域变化
WO_RGN_SURFACE_DELTA 表面区域增量变化
WO_DRAW_NOTIFY 绘制通知
WO_SPRITE_NOTIFY 精灵重叠通知
WO_RGN_WINDOW / WO_RGN_DESKTOP_COORD / WO_RGN_UPDATE_ALL 未实现(FIXME)

引擎触发的回调标志(WOC_*):

标志 含义
WOC_RGN_CLIENT 客户区可见区域已更新
WOC_CHANGED 窗口已变化
WOC_DELETE 窗口对象将被删除
WOC_DRAWN 已绘制
WOC_SPRITE_OVERLAP / WOC_SPRITE_NO_OVERLAP 精灵重叠/不重叠

3. 核心函数分析

3.1 创建 --- EngCreateWnd

#mermaid-svg-90LI8skYhfXd5Ov9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-90LI8skYhfXd5Ov9 .error-icon{fill:#552222;}#mermaid-svg-90LI8skYhfXd5Ov9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-90LI8skYhfXd5Ov9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-90LI8skYhfXd5Ov9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-90LI8skYhfXd5Ov9 .marker.cross{stroke:#333333;}#mermaid-svg-90LI8skYhfXd5Ov9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-90LI8skYhfXd5Ov9 p{margin:0;}#mermaid-svg-90LI8skYhfXd5Ov9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-90LI8skYhfXd5Ov9 .cluster-label text{fill:#333;}#mermaid-svg-90LI8skYhfXd5Ov9 .cluster-label span{color:#333;}#mermaid-svg-90LI8skYhfXd5Ov9 .cluster-label span p{background-color:transparent;}#mermaid-svg-90LI8skYhfXd5Ov9 .label text,#mermaid-svg-90LI8skYhfXd5Ov9 span{fill:#333;color:#333;}#mermaid-svg-90LI8skYhfXd5Ov9 .node rect,#mermaid-svg-90LI8skYhfXd5Ov9 .node circle,#mermaid-svg-90LI8skYhfXd5Ov9 .node ellipse,#mermaid-svg-90LI8skYhfXd5Ov9 .node polygon,#mermaid-svg-90LI8skYhfXd5Ov9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-90LI8skYhfXd5Ov9 .rough-node .label text,#mermaid-svg-90LI8skYhfXd5Ov9 .node .label text,#mermaid-svg-90LI8skYhfXd5Ov9 .image-shape .label,#mermaid-svg-90LI8skYhfXd5Ov9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-90LI8skYhfXd5Ov9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-90LI8skYhfXd5Ov9 .rough-node .label,#mermaid-svg-90LI8skYhfXd5Ov9 .node .label,#mermaid-svg-90LI8skYhfXd5Ov9 .image-shape .label,#mermaid-svg-90LI8skYhfXd5Ov9 .icon-shape .label{text-align:center;}#mermaid-svg-90LI8skYhfXd5Ov9 .node.clickable{cursor:pointer;}#mermaid-svg-90LI8skYhfXd5Ov9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-90LI8skYhfXd5Ov9 .arrowheadPath{fill:#333333;}#mermaid-svg-90LI8skYhfXd5Ov9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-90LI8skYhfXd5Ov9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-90LI8skYhfXd5Ov9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-90LI8skYhfXd5Ov9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-90LI8skYhfXd5Ov9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-90LI8skYhfXd5Ov9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-90LI8skYhfXd5Ov9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-90LI8skYhfXd5Ov9 .cluster text{fill:#333;}#mermaid-svg-90LI8skYhfXd5Ov9 .cluster span{color:#333;}#mermaid-svg-90LI8skYhfXd5Ov9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-90LI8skYhfXd5Ov9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-90LI8skYhfXd5Ov9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-90LI8skYhfXd5Ov9 .icon-shape,#mermaid-svg-90LI8skYhfXd5Ov9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-90LI8skYhfXd5Ov9 .icon-shape p,#mermaid-svg-90LI8skYhfXd5Ov9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-90LI8skYhfXd5Ov9 .icon-shape .label rect,#mermaid-svg-90LI8skYhfXd5Ov9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-90LI8skYhfXd5Ov9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-90LI8skYhfXd5Ov9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-90LI8skYhfXd5Ov9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

失败
失败
EngCreateWnd

(pso, hWnd, pfn, fl, iPixelFormat)
UserEnterExclusive

(用户子系统独占锁)
fl 含未支持标志?

(WO_RGN_WINDOW 等)
FIXME 警告
继续
UserGetWindowObject(hWnd)

获取窗口对象
退出返回 NULL
分配 EWNDOBJ

EngAllocMem
IntEngInitClipObj

(Rects 指向内嵌 rclBounds)
IntEngWndUpdateClipObj

用窗口可见区域填充裁剪对象
释放内存
填充 DDI 公开部分

psoOwner = pso, pvConsumer = NULL
填充内部字段

Hwnd/ChangeProc/Flags/PixelFormat
UserSetProp 关联到窗口

(AtomWndObj 属性)
gcountPWO++
UserLeave 返回 WNDOBJ

关键代码:

c 复制代码
WNDOBJ* APIENTRY EngCreateWnd(SURFOBJ *pso, HWND hWnd, WNDOBJCHANGEPROC pfn,
                              FLONG fl, int iPixelFormat)
{
    Window = UserGetWindowObject(hWnd);           // 1. 取窗口对象
    if (Window == NULL) goto Exit;

    Clip = EngAllocMem(FL_ZERO_MEMORY, sizeof(EWNDOBJ), GDITAG_WNDOBJ);
    IntEngInitClipObj((XCLIPOBJ*)Clip);           // 2. 初始化裁剪对象

    /* 3. 用窗口可见区域填充裁剪对象 */
    if (!IntEngWndUpdateClipObj(Clip, Window)) { EngFreeMem(Clip); goto Exit; }

    /* 4. 填充 DDI 公开部分 */
    WndObjUser = (WNDOBJ *)Clip;
    WndObjUser->psoOwner = pso;
    WndObjUser->pvConsumer = NULL;

    /* 5. 填充内部字段 */
    Clip->Hwnd = hWnd;
    Clip->ChangeProc = pfn;
    Clip->Flags = fl & (WO_RGN_CLIENT_DELTA | WO_RGN_CLIENT | WO_RGN_SURFACE_DELTA |
                        WO_RGN_SURFACE | WO_DRAW_NOTIFY);
    if (fl & WO_SPRITE_NOTIFY)
        Clip->Flags |= WOC_SPRITE_OVERLAP | WOC_SPRITE_NO_OVERLAP;
    Clip->Flags |= WOC_CHANGED | WOC_DELETE;      // 始终发送
    Clip->PixelFormat = iPixelFormat;

    /* 6. 通过窗口属性关联到窗口 */
    UserSetProp(Window, AtomWndObj, Clip, TRUE);
    ++gcountPWO;
    return WndObjUser;
}

要点:

  • 只保存驱动请求的 标志(fl & WO_*),回调时按此过滤
  • WOC_CHANGED/WOC_DELETE 始终加入 Flags(这些通知必须发送)
  • 通过 AtomWndObj 窗口属性把 EWNDOBJ 挂到窗口上,窗口变化时能找回

3.2 从窗口更新裁剪对象 --- IntEngWndUpdateClipObj

c 复制代码
BOOLEAN FASTCALL IntEngWndUpdateClipObj(EWNDOBJ* Clip, PWND Window)
{
    /* 计算窗口的可见区域(考虑兄弟窗口遮挡) */
    visRgn = VIS_ComputeVisibleRegion(Window, TRUE, TRUE, TRUE);
    if (visRgn != NULL && visRgn->rdh.nCount > 0)
    {
        /* 可见区域 → 裁剪对象(多矩形 → DC_COMPLEX) */
        IntEngUpdateClipRegion((XCLIPOBJ*)Clip, visRgn->rdh.nCount,
                               visRgn->Buffer, &visRgn->rdh.rcBound);
        REGION_Delete(visRgn);
    }
    else
    {
        /* 退化为客户区矩形 */
        IntEngUpdateClipRegion((XCLIPOBJ*)Clip, 1, &Window->rcClient, &Window->rcClient);
    }

    /* 更新客户区矩形和唯一性标志 */
    Clip->rclClient = Window->rcClient;
    Clip->iUniq++;
    return TRUE;
}

要点: VIS_ComputeVisibleRegion 计算窗口的真实可见区域 (扣除被覆盖部分)------多矩形时裁剪对象变为 DC_COMPLEX,驱动用 WNDOBJ_bEnum 枚举可见矩形只绘制可见部分。

3.3 窗口变化通知 --- IntEngWindowChanged

窗口移动/改变尺寸/删除时由 ntuser 调用:

c 复制代码
VOID FASTCALL IntEngWindowChanged(PWND Window, FLONG flChanged)
{
    Clip = UserGetProp(Window, AtomWndObj, TRUE);   // 找回 WNDOBJ
    if (!Clip) return;

    switch (flChanged)
    {
    case WOC_RGN_CLIENT:
        /* 更新裁剪对象和客户区 */
        IntEngWndUpdateClipObj(Clip, Window);
        break;
    case WOC_DELETE:
        /* FIXME: WNDOBJ 由 win32k 还是驱动删除? */
        break;
    }

    /* 调用驱动回调 */
    IntEngWndCallChangeProc(Clip, flChanged);

    /* HACK: WOC_RGN_CLIENT 后再发 WOC_CHANGED */
    if (flChanged == WOC_RGN_CLIENT)
        IntEngWndCallChangeProc(Clip, WOC_CHANGED);
}

3.4 回调调度 --- IntEngWndCallChangeProc

c 复制代码
VOID FASTCALL IntEngWndCallChangeProc(EWNDOBJ *Clip, FLONG flChanged)
{
    if (Clip->ChangeProc == NULL) return;

    /* 按驱动请求的标志过滤 */
    flChanged &= Clip->Flags;
    if (flChanged == 0) return;

    /* WOC_CHANGED 无矩形数据,传 NULL;其余传 WNDOBJ 指针 */
    if (flChanged == WOC_CHANGED)
        Clip->ChangeProc(NULL, flChanged);
    else
        Clip->ChangeProc((WNDOBJ *)Clip, flChanged);
}

3.5 删除 --- EngDeleteWnd

c 复制代码
VOID APIENTRY EngDeleteWnd(WNDOBJ *pwo)
{
    Clip = (EWNDOBJ *)pwo;

    /* 从窗口移除属性 */
    Window = UserGetWindowObject(Clip->Hwnd);
    if (Window) UserRemoveProp(Window, AtomWndObj, TRUE);
    --gcountPWO;

    /* 释放资源(Rects 堆数组 + 对象本身) */
    IntEngFreeClipResources((XCLIPOBJ*)Clip);
    EngFreeMem(Clip);
}

3.6 DDI 枚举函数(驱动侧)

c 复制代码
/* 枚举可见矩形(转给 CLIPOBJ_bEnum) */
BOOL APIENTRY WNDOBJ_bEnum(WNDOBJ *pwo, ULONG cj, ULONG *pul)
{
    return CLIPOBJ_bEnum(&pwo->coClient, cj, pul);
}

/* 枚举初始化(转给 CLIPOBJ_cEnumStart) */
ULONG APIENTRY WNDOBJ_cEnumStart(WNDOBJ *pwo, ULONG iType, ULONG iDirection, ULONG cLimit)
{
    return CLIPOBJ_cEnumStart(&pwo->coClient, FALSE, iType, iDirection, cLimit);
}

要点: WNDOBJ 的枚举完全复用 CLIPOBJ 机制------WNDOBJ 本质就是带窗口语义的 CLIPOBJ。

3.7 设置消费者 --- WNDOBJ_vSetConsumer

c 复制代码
VOID APIENTRY WNDOBJ_vSetConsumer(WNDOBJ *pwo, PVOID pvConsumer)
{
    Hack = (pwo->pvConsumer == NULL);
    pwo->pvConsumer = pvConsumer;      // 保存驱动私有数据

    /* HACK: 首次设置时补发初始状态(MSDN 要求创建后回调最近状态) */
    if (Hack)
    {
        IntEngWndCallChangeProc(Clip, WOC_RGN_CLIENT);
        IntEngWndCallChangeProc(Clip, WOC_CHANGED);
        IntEngWndCallChangeProc(Clip, WOC_DRAWN);
    }
}

4. 数据流(窗口变化 → 驱动收到通知)

显示驱动 engwindow.c ntuser (WND 窗口) 显示驱动 engwindow.c ntuser (WND 窗口) #mermaid-svg-tgReH6sve9pQDnEC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tgReH6sve9pQDnEC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tgReH6sve9pQDnEC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tgReH6sve9pQDnEC .error-icon{fill:#552222;}#mermaid-svg-tgReH6sve9pQDnEC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tgReH6sve9pQDnEC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tgReH6sve9pQDnEC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tgReH6sve9pQDnEC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tgReH6sve9pQDnEC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tgReH6sve9pQDnEC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tgReH6sve9pQDnEC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tgReH6sve9pQDnEC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tgReH6sve9pQDnEC .marker.cross{stroke:#333333;}#mermaid-svg-tgReH6sve9pQDnEC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tgReH6sve9pQDnEC p{margin:0;}#mermaid-svg-tgReH6sve9pQDnEC .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tgReH6sve9pQDnEC text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tgReH6sve9pQDnEC .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tgReH6sve9pQDnEC .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tgReH6sve9pQDnEC .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tgReH6sve9pQDnEC .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tgReH6sve9pQDnEC #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tgReH6sve9pQDnEC .sequenceNumber{fill:white;}#mermaid-svg-tgReH6sve9pQDnEC #sequencenumber{fill:#333;}#mermaid-svg-tgReH6sve9pQDnEC #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tgReH6sve9pQDnEC .messageText{fill:#333;stroke:none;}#mermaid-svg-tgReH6sve9pQDnEC .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tgReH6sve9pQDnEC .labelText,#mermaid-svg-tgReH6sve9pQDnEC .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tgReH6sve9pQDnEC .loopText,#mermaid-svg-tgReH6sve9pQDnEC .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tgReH6sve9pQDnEC .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tgReH6sve9pQDnEC .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tgReH6sve9pQDnEC .noteText,#mermaid-svg-tgReH6sve9pQDnEC .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tgReH6sve9pQDnEC .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tgReH6sve9pQDnEC .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tgReH6sve9pQDnEC .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tgReH6sve9pQDnEC .actorPopupMenu{position:absolute;}#mermaid-svg-tgReH6sve9pQDnEC .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-tgReH6sve9pQDnEC .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tgReH6sve9pQDnEC .actor-man circle,#mermaid-svg-tgReH6sve9pQDnEC line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tgReH6sve9pQDnEC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 驱动创建窗口表面 窗口移动/改变尺寸 驱动处理 销毁 EngCreateWnd (pso, hWnd, ChangeProc, fl)UserGetWindowObject + VIS_ComputeVisibleRegionWNDOBJ (含可见区域 CLIPOBJ)IntEngWindowChanged (WOC_RGN_CLIENT)VIS_ComputeVisibleRegion (重新计算)IntEngWndUpdateClipObj (更新裁剪对象 + iUniq++)ChangeProc(WNDOBJ, WOC_RGN_CLIENT)ChangeProc(NULL, WOC_CHANGED)WNDOBJ_cEnumStart / WNDOBJ_bEnum (枚举可见矩形)可见矩形列表只重绘可见区域 (硬件加速)EngDeleteWndUserRemoveProp (解除关联)


5. 设计特点总结

  1. WNDOBJ 即特殊 CLIPOBJ:窗口对象继承 CLIPOBJ,裁剪区域 = 窗口可见区域,枚举完全复用 CLIPOBJ 机制。
  2. 可见区域计算VIS_ComputeVisibleRegion 计算扣除遮挡后的真实可见区域(多矩形 → DC_COMPLEX)。
  3. 回调过滤 :只发送驱动请求的标志(flChanged &= Clip->Flags),减少无关通知。
  4. 窗口属性关联 :通过 AtomWndObj 属性把 WNDOBJ 挂到窗口,窗口变化可快速找回。
  5. iUniq 失效标记 :裁剪区域每次更新 iUniq++,驱动可据此判断缓存是否失效。
  6. HACK 兼容:创建/设置消费者时补发初始状态通知,符合 MSDN 对 WNDOBJCHANGEPROC 的约定。
  7. 精灵通知预留WO_SPRITE_NOTIFY 支持精灵重叠跟踪(未完整实现)。

6. 源码索引

位置 关键内容
engwindow.c(file:///d:/reactos/win32ss/gdi/eng/engwindow.c#L18-L44) IntEngWndCallChangeProc(回调过滤调度)
engwindow.c(file:///d:/reactos/win32ss/gdi/eng/engwindow.c#L49-L92) IntEngWndUpdateClipObj(可见区域 → 裁剪对象)
engwindow.c(file:///d:/reactos/win32ss/gdi/eng/engwindow.c#L97-L138) IntEngWindowChanged(窗口变化通知)
engwindow.c(file:///d:/reactos/win32ss/gdi/eng/engwindow.c#L143-L214) EngCreateWnd(创建)
engwindow.c(file:///d:/reactos/win32ss/gdi/eng/engwindow.c#L220-L250) EngDeleteWnd(删除)
engwindow.c(file:///d:/reactos/win32ss/gdi/eng/engwindow.c#L256-L317) WNDOBJ_bEnum / cEnumStart / vSetConsumer
engobjects.h(file:///d:/reactos/win32ss/gdi/eng/engobjects.h#L42-L46) RWNDOBJ 结构
engobjects.h(file:///d:/reactos/win32ss/gdi/eng/engobjects.h#L92-L105) EWNDOBJ 结构

关联模块:

  • eng/clip.c(file:///d:/reactos/win32ss/gdi/eng/clip.c) --- CLIPOBJ 机制(WNDOBJ 的继承基础)
  • ntuser/window.c(file:///d:/reactos/win32ss/user/ntuser/window.c) --- WND 窗口对象(上游)
  • ntuser/vis.c(file:///d:/reactos/win32ss/user/ntuser/vis.c) --- VIS_ComputeVisibleRegion 可见区域计算

关联文档:

  • 《ReactOS 图形系统分析_4.md》--- GDI 引擎整体架构(本文件是窗口对象子模块)
  • 《ReactOS 图形系统分析_8.md》--- 裁剪管理(WNDOBJ 复用的 CLIPOBJ 机制)

本文档基于 ReactOS 源代码 win32ss/gdi/eng/engwindow.c 分析(2026 年 8 月)

相关推荐
青 春 记 忆1 小时前
零基础入门python07:让程序记住数据——JSON文件和异常处理
开发语言·windows·python·json·python3.11
W_326001 小时前
Python 常用标准 / 第三方库:random、tqdm、turtle、jieba 用法
开发语言·python
caimouse2 小时前
ReactOS 图形系统分析(17):区域填充 — EngPaint / IntEngPaint(paint.c)
c语言·开发语言·算法
北风toto2 小时前
研发效能与后端核心技术全景指南:从CI/CD到共性组件实战
java·开发语言·ci/cd
Billy121382 小时前
Day12-C++20 Coroutines(上):协程原理与Promise/Awaitable机制
开发语言·c++进阶学习
01二进制代码漫游日记2 小时前
C++基础入门速通
java·开发语言·c++
Herbert_hwt2 小时前
第六章 Java深入理解接口、函数式接口与lambda表达式
java·开发语言·算法
橙橙笔记2 小时前
C++的学习第三部分
开发语言·c++·学习
2603_9651481111 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api