当前位置: 首页 > news >正文

ReactOS 图形系统分析(25):多显示器/平移显示与杂项 — multidisp.c / pandisp.c / engmisc.c

ReactOS 图形系统分析(25):多显示器/平移显示与杂项 — multidisp.c / pandisp.c / engmisc.c

1. 概述

《分析_4》第 45~46 行把multidisp.c/pandisp.c(多显示器/平移显示)与engmisc.c(杂项)并列。三者实为三种角色:

文件角色现状
[multidisp.c](file:///d:/reactos/win32ss/gdi/eng/multidisp.c)多显示器(META)显示驱动入口MultiEnableDriverUNIMPLEMENTED 桩(仅声明,恒 FALSE)
[pandisp.c](file:///d:/reactos/win32ss/gdi/eng/pandisp.c)平移显示(Panning Display)驱动:桌面大于屏幕时,用"视口"漫游大桌面完整实现(gPanDispDrvFn 13 个 DDI)
[engmisc.c](file:///d:/reactos/win32ss/gdi/eng/engmisc.c)引擎杂项服务:设备表面"进入/离开"、系统查询、代码页、tick全部实现

平移显示(pandisp.c)是核心:它包装一个真实显示驱动(underlying PDEV),对外提供更大的虚拟桌面,内部把绘制重定向到"影子表面(shadow)",再按需把**视口(viewport)**区域同步到物理屏幕。用户移动鼠标到屏幕边缘时视口随之滚动——这就是DEVMODE.dmPanningWidth/dmPanningHeight的语义。

DEVMODE: dmPelsWidth×dmPelsHeight(虚拟桌面) > dmPanningWidth×dmPanningHeight(物理屏幕) ┌─────────────────────────── 虚拟桌面 (szlDesktop) ───────────────────────────┐ │ │ │ ┌─────────────── 视口 rclViewport(物理屏幕大小) ───────────────┐ │ │ │ 光标移到边缘 → 视口滚动 (PanMovePointer) │ │ │ │ 绘制 → 影子表面 psoShadow → PanSynchronize 同步视口区域到屏幕 │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────────────┘

2. 加载机制(pdevobj.c 的 vtable 替换)

[PDEVOBJ_pLoadPDEV/pEnableDisplay](file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L511-L575) 决定何时启用平移:

/* 1. META 设备 → 多显示器驱动(桩) */if(ldevtype==LDEV_DEVICE_META)pldev=LDEVOBJ_pLoadInternal(MultiEnableDriver,ldevtype);/* 2. 显示设备且请求了平移尺寸 → 用平移 vtable 覆盖驱动函数表 */if(ldevtype==LDEV_DEVICE_DISPLAY&&(dwAccelerationLevel>=5||pdm->dmFields&(DM_PANNINGWIDTH|DM_PANNINGHEIGHT))){/* 缺省填平:dmPanning* = dmPels* */if(!(pdm->dmFields&DM_PANNINGWIDTH))pdm->dmPanningWidth=pdm->dmPelsWidth;if(!(pdm->dmFields&DM_PANNINGHEIGHT))pdm->dmPanningHeight=pdm->dmPelsHeight;/* 用 gPanDispDrvFn 覆盖 ppdev->apfn 对应项 */for(i=0;i<gPanDispDrvCount;i++)ppdev->apfn[gPanDispDrvFn[i].iFunc]=gPanDispDrvFn[i].pfn;}elseppdev->pfn=ppdev->pldev->pfn;/* 普通驱动函数表 */

关键:平移驱动不是独立驱动模块,而是同一 PDEV 上对底层驱动函数表的包装层(vtable 替换),无需加载新 DLL。PANDEV保存了底层驱动的完整apfn[INDEX_LAST]表(用GETPFN(name)宏回调底层)。


3. PANDEV 结构与生命周期

3.1 PANDEV([pandisp.c](file:///d:/reactos/win32ss/gdi/eng/pandisp.c#L19-L36))

typedefstruct_PANDEV{ULONG iBitmapFormat;// 底层表面格式HDEV hdev;SIZEL szlDesktop;// 虚拟桌面大小(dmPelsW×H)RECTL rclViewport;// 视口(物理屏幕大小的漫游窗口)HSURF hsurf;// 设备表面(对引擎暴露,桌面大小)HSURF hsurfShadow;// 影子表面(实际绘制目标,内存位图)SURFOBJ*psoShadow;/* 底层 PDEV */ULONG flUnderlyingGraphicsCaps;SIZEL szlScreen;// 物理屏幕大小(dmPanningW×H)DHPDEV dhpdevScreen;// 底层 PDEV 句柄BOOL enabledScreen;// 底层表面是否已启用SURFOBJ*surfObjScreen;// 底层屏幕表面PFN apfn[INDEX_LAST];// 底层驱动函数表(包装回调用)}PANDEV,*PPANDEV;

3.2 生命周期 DDI

函数行为
PanEnablePDEVdmPanningW×H的 DEVMODE启用底层 PDEVGETPFN(EnablePDEV)),保存底层函数表;把上报给 GDI 的分辨率升级为虚拟桌面dmPelsW×H;置GCAPS_PANNING+GCAPS2_SYNCFLUSH|SYNCTIMER
PanCompletePDEV/PanDisablePDEV透传底层CompletePDEV/DisablePDEV,释放 PANDEV
PanEnableSurface① 启用底层表面并锁定(surfObjScreen)②EngCreateDeviceSurface建桌面大小设备表面 ③EngAssociateSurface挂钩 HOOK_BITBLT/COPYBITS/ALPHABLEND/GRADIENTFILL/STROKEPATH/SYNCHRONIZE/TEXTOUT ④EngCreateBitmap建影子表面(桌面大小,BMF_TOPDOWN);视口初始居中
PanDisableSurface逆序清理(可被PanEnableSurface的失败路径调用,故各步判空)

设计:对 GDI/引擎而言,设备表面是桌面大小;而真正可见的物理屏幕只是其中rclViewport一块。引擎的绘制都发生在影子表面(引擎可直接读写内存位图),再由同步机制拷到物理屏。


4. 绘制重定向与同步

4.1 重定向到影子表面

PanBitBlt(psoTrg,psoSrc,...){/* 设备表面 → 影子表面(源/目标都换) */if(psoTrg->iType==STYPE_DEVICE)psoTrg=pandev->psoShadow;if(psoSrc&&psoSrc->iType==STYPE_DEVICE)psoSrc=pandev->psoShadow;res=EngBitBlt(psoTrg,psoSrc,...);// 引擎软件绘制if(res)PanSynchronize(gPan,NULL);// 绘制完立刻同步(见下)returnres;}

同类包装:PanCopyBits(→PanBitBlt,ROP4_SRCCOPY)、PanAlphaBlend(→EngAlphaBlend到影子)、PanGradientFill(→EngGradientFill到影子)。PanStrokePath/PanTextOut未实现(UNIMPLEMENTED + ASSERT(FALSE))。

4.2 同步 — PanSynchronize

PanSynchronize(dhpdev,prcl){/* FIXME: 当前整块拷贝影子缓冲,未按 prcl 局部优化 */ptlSrc=rclViewport.left/top;// 视口左上角rclDest=0,0,szlScreen.cx,szlScreen.cy;// 整块物理屏幕EngCopyBits(surfObjScreen,psoShadow,NULL,NULL,&rclDest,&ptlSrc);}

把影子表面中视口区域拷贝到物理屏幕表面(EngCopyBits)。注册为INDEX_DrvSynchronize,同时GCAPS2_SYNCFLUSH让引擎在绘制后调用;但源码 FIXME 明确:当前靠各绘制函数手动调用GCAPS2_SYNCTIMER定时同步尚未实现,用全局gPan兜底)。


5. 视口漫游 — PanMovePointer

光标移到屏幕边缘时移动视口(GCAPS_PANNING语义):

PanMovePointer(pso,x,y,prcl){/* 忽略隐藏/平移通知(无 DrvSetPointerShape,ASSERT x>=0 && y<0) */if(x<0||y>=0)return;if(底层支持 GCAPS_PANNING)底层MovePointer(...);// 透传底层光标y+=pso->sizlBitmap.cy;// 光标 y 相对屏幕底部换算/* 视口跟随光标:越左/右/上/下边缘则滚动 */if(x<rclViewport.left){rclViewport.left=x;right=x+szlScreen.cx;bChanged=TRUE;}elseif(x>=rclViewport.right){right=x+1;left=x-szlScreen.cx;bChanged=TRUE;}/* y 同理 */if(bChanged)PanSynchronize(dhpdev,NULL);// 滚动后立即同步}

6. 多显示器 — multidisp.c(桩)

MultiEnableDriverLDEV_DEVICE_META(多显示器设备)的驱动入口——Windows 中由它聚合多个 PDEV 成统一桌面(与MDEVOBJ多显示器管理配套)。ReactOS 当前为UNIMPLEMENTED桩(恒返回 FALSE),即META 设备路径未启用,多显示器目前只靠mdevobj.c的 MDEV 映射对象做基础支持。


7. 杂项 — engmisc.c

7.1 IntEngEnter / IntEngLeave — 设备表面的"进入/离开"(核心工具)

问题:引擎软件绘制(BitBlt/AlphaBlend/StretchBlt/LineTo/TransBlt/Gradient)需要能直接读写像素pvScan0/lDelta)的位图表面;而设备管理表面(无像素缓冲)无法直接操作。

IntEngEnter 解决:把设备表面换成"临时内存位图"再操作,结束后IntEngLeaveDrvCopyBits拷回:

IntEngEnter(&el,psoDest,&DestRect,ReadOnly,&Translate,&psoOutput){if(设备表面&&无 pvScan0){if(!HOOK_COPYBITS)returnFALSE;// 驱动不支持 CopyBits 则失败psoOutput=EngCreateBitmap(...);// 临时位图(目标区域大小)GDIDEVFUNCS(psoDest).CopyBits(psoOutput,psoDest,...);// 先把设备内容拷入Translate=-DestRect.left/top;// 坐标系平移到临时位图}elsepsoOutput=psoDest;// 位图表面直接用/* 若挂钩 HOOK_SYNCHRONIZE → 调用 SynchronizeSurface/Synchronize */}IntEngLeave(&el){if(OutputObj!=DestObj)// 用了临时位图{if(!ReadOnly)GDIDEVFUNCS(DestObj).CopyBits(DestObj,OutputObj,...);// 结果拷回设备表面释放临时位图+TrivialClipObj;}}

调用方(36 处):[bitblt.c](file:///d:/reactos/win32ss/gdi/eng/bitblt.c#L965)、[alphablend.c](file:///d:/reactos/win32ss/gdi/eng/alphablend.c#L103)、[stretchblt.c](file:///d:/reactos/win32ss/gdi/eng/stretchblt.c#L205)、[lineto.c](file:///d:/reactos/win32ss/gdi/eng/lineto.c#L553)、[transblt.c](file:///d:/reactos/win32ss/gdi/eng/transblt.c#L40)、[gradient.c](file:///d:/reactos/win32ss/gdi/eng/gradient.c#L85)——这正是"引擎软件渲染能作用于设备表面"的通用桥梁(pandisp 的PanBitBlt回调EngBitBlt时即受益)。

7.2 其他杂项函数

函数行为
EngGetProcessHandle恒返回 NULL(Win2K 起废弃,ABI 保留)
EngGetCurrentCodePage转发RtlGetDefaultCodePage返回 OEM/ANSI 代码页([winddi.h](file:///d:/reactos/sdk/include/psdk/winddi.h) DDI 服务)
EngQuerySystemAttribute查询系统属性:EngNumberOfProcessors(NtQuerySystemInformation Basic)→ CPU 数;EngProcessorFeature(Processor 信息)→ 特性位
EngGetTickCountKeQueryTickCount+KiTickCountToMs返回毫秒 tick

8. 与《分析_4》标注对照

标注核对
《_4》第 45 行:multidisp.c/pandisp.c =「多显示器/平移显示」基本准确。补充:multidisp.c 是META 设备入口(桩);真正实现是 pandisp.c(平移显示 vtable 包装层);多显示器聚合另见 mdevobj.c(MDEVOBJ)
《_4》第 46 行:engmisc.c =「杂项」准确。核心是IntEngEnter/IntEngLeave(设备表面 ↔ 临时位图的 CopyBits 桥梁),另有系统查询/代码页/tick

9. 源码索引

文件关键内容
[pandisp.c](file:///d:/reactos/win32ss/gdi/eng/pandisp.c)PANDEV、PanEnablePDEV/EnableSurface/DisableSurface、PanBitBlt/PanCopyBits/PanAlphaBlend/PanGradientFill、PanSynchronize、PanMovePointer、gPanDispDrvFn(13 项)
[multidisp.c](file:///d:/reactos/win32ss/gdi/eng/multidisp.c)MultiEnableDriver(LDEV_DEVICE_META 入口,桩)
[engmisc.c](file:///d:/reactos/win32ss/gdi/eng/engmisc.c)IntEngEnter/IntEngLeave、EngGetProcessHandle、EngGetCurrentCodePage、EngQuerySystemAttribute、EngGetTickCount
[pdevobj.c](file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L511-L575)平移 vtable 替换逻辑、META 驱动加载
[mdevobj.c](file:///d:/reactos/win32ss/gdi/eng/mdevobj.c) / mdevobj.hMDEVOBJ 多显示器映射对象
[ntgdi/misc.h](file:///d:/reactos/win32ss/gdi/ntgdi/misc.h#L14-L21)IntEngEnter/IntEngLeave 声明

关联模块:

  • 底层回调:EngBitBlt/EngAlphaBlend/EngGradientFill/EngCopyBits(《分析_4》3.1)
  • 加载入口:LDEVOBJ_pLoadInternal(《分析_4》3.4)
  • 设备对象:PDEVOBJ(ppdev->apfn 被替换)
  • 光标联动:PanMovePointer与《分析_24》鼠标管理的交互(FIXME 提及 MouseSafety 误调)

10. 各函数作用和使用

10.1 pandisp.c — 平移显示驱动(DDI 函数表 gPanDispDrvFn)

函数作用使用方式/要点
PanEnablePDEV启用平移设备:以dmPanningW×H启用底层 PDEV,上报桌面尺寸dmPelsW×H由引擎经PDEVOBJ_bEnablePDEV调用;内部回调底层EnablePDEV并保存其apfn表;置GCAPS_PANNING/GCAPS2_SYNCFLUSH|SYNCTIMER。失败返回 NULL(已释放 PANDEV)
PanCompletePDEV设备就绪通知透传底层CompletePDEV;保存hdev
PanDisablePDEV卸载设备回调底层DisablePDEVEngFreeMem释放 PANDEV
PanEnableSurface启用表面:建设备表面(桌面尺寸)+影子表面+ 挂钩 HOOK_*引擎在启用显示后调用;失败路径会自行调用PanDisableSurface清理(故后者须容错未初始化状态)
PanDisableSurface逆序清理所有表面/影子/底层表面各步判空,可被PanEnableSurface失败路径复用
PanSynchronize把影子表面视口区域EngCopyBits到物理屏幕注册为DrvSynchronize;当前由各绘制函数手动调用(FIXME:应定时同步)
PanBitBlt位块传输:设备表面→影子表面,绘制后同步引擎绘制 DDI 回调;源/目标若是设备表面则替换为影子表面,再调EngBitBlt,成功后PanSynchronize
PanCopyBits表面拷贝简化为PanBitBlt(..., ROP4_SRCCOPY)DrvCopyBits回调)
PanStrokePath路径描边UNIMPLEMENTED(ASSERT(FALSE),当前不可用)
PanTextOut文本输出UNIMPLEMENTED(ASSERT(FALSE),当前不可用)
PanMovePointer光标移动 +视口漫游DrvMovePointer回调;x<0(隐藏)或y>=0(平移通知)直接忽略;光标触边则滚动rclViewportPanSynchronize;底层支持GCAPS_PANNING时先透传光标
PanAlphaBlendAlpha 混合到影子表面后同步引擎回调;调EngAlphaBlend(psoShadow, ...)PanSynchronize
PanGradientFill渐变填充到影子表面后同步引擎回调;调EngGradientFill(psoShadow, ...)PanSynchronize

共用说明:GETPFN(name)宏(pandev->apfn[INDEX_Drv##name])用于回调底层驱动同名 DDI——所有 Pan* 函数都是"底层驱动函数的包装层"。

10.2 multidisp.c — 多显示器驱动入口

函数作用使用方式/要点
MultiEnableDriver多显示器(META 设备)驱动加载入口UNIMPLEMENTED 桩,恒返回 FALSE。仅当LDEV_DEVICE_META时由LDEVOBJ_pLoadInternal引用;实现前 META 设备路径不可用

10.3 engmisc.c — 引擎杂项

函数作用使用方式/要点
IntEngEnter设备表面"进入":把不可直接读写的设备表面换成临时内存位图,供引擎软件绘制内部函数([misc.h](file:///d:/reactos/win32ss/gdi/ntgdi/misc.h#L14-L21) 声明);bitblt/alphablend/stretchblt/lineto/transblt/gradient 等绘制前成对调用。前置条件:设备表面须挂钩HOOK_COPYBITS,否则返回 FALSE;输出Translate用于坐标平移;成功后若挂钩HOOK_SYNCHRONIZE会先同步
IntEngLeave设备表面"离开":把临时位图结果经DrvCopyBits拷回设备表面并释放临时资源必须与IntEngEnter严格成对ReadOnly为 TRUE 时不做回拷);漏调会泄漏位图/裁剪对象
EngGetProcessHandle获取进程句柄恒返回 NULL(Win2K 起废弃,驱动勿依赖)
EngGetCurrentCodePage查询 OEM/ANSI 代码页DDI 服务(win32k 导出);转发RtlGetDefaultCodePage
EngQuerySystemAttribute查询系统属性驱动用CapNum=EngNumberOfProcessors取 CPU 数、EngProcessorFeature取处理器特性位;其余枚举返回 FALSE
EngGetTickCount获取系统 tick(毫秒)KeQueryTickCount+KiTickCountToMs;驱动计时用

本文档基于 ReactOS 源代码win32ss/gdi/eng/multidisp.cpandisp.cengmisc.c及关联模块分析(2026 年 8 月)

http://www.cnnetsun.cn/news/4047711.html

相关文章:

  • 193、飞控中的无人机集群:未来趋势与挑战
  • 基于Springboot的小香葱种植管理系统源码+文档
  • Turnitin降AI英文怎么改,BunnyScholar最省心
  • 【双层规划,节点出清价,绿证交易,CVaR方法】两级电力市场环境下计及风险的省间交易商最优购电模型附Matlab代码
  • 云安全左移:解析默认防火墙与API开放对运维的影响
  • Hive存储格式深度解析:从TextFile到ORC/Parquet的性能调优实战
  • UML用例图实战指南:从需求沟通到系统设计的可视化建模
  • LaTeX表格加粗排版难题:原理剖析与四种稳健解决方案
  • PostgreSQL启动失败排查指南:从日志分析到六大常见原因解决
  • SpringBoot集成Druid监控:Web界面配置、SQL性能分析与生产安全实践
  • AI 自动化工具 OpenClaw 实操:从解压到正常使用完整记录(含安装包)
  • 召回系统数据准备:YAML配置驱动与Pydantic验证实践
  • 变压器分类
  • HTML5前端开发:从基础到企业级实践指南
  • 6.3 显存与地址:amd_memory
  • C-05. Kernel Fusion 代价边界:少写回 vs 寄存器压力与 occupancy
  • 04-人脸对齐与ArcFace识别
  • 告别双电机“较劲”,MOTEC主从控制模式让驱动“完美”同步。
  • MySQL安全配置:secure-file-priv原理、配置与实战指南
  • 做弱电十年,筛选长期合作一级代理商核心条件
  • ARM Cortex-A/R/M内核深度解析:从架构差异到实战选型指南
  • 基于Python与AI的邮件日程自动化助手:从零构建智能联动原型
  • AI研发框架重构Git工作流:提升67%代码审查效率
  • 第四篇 STM32MP157-M4:Makefile 完整详解
  • 【太狠了】做自媒体多平台发布太耗时?一键同步公众号、知乎、小红书8个主流平台
  • 基于MiniCPM5-1B构建本地研究智能体:从模型部署到ReAct框架实战
  • 第2章 坤•承载 二维的答案与三维的深渊
  • Git分支管理:从创建、拉取到跟踪的完整实践指南
  • MMKV原理与实战:高性能键值存储组件深度解析
  • 钉钉直播教学全流程26个常见问题解决方案与实战指南