dantiicu/wine-nx
GitHub: dantiicu/wine-nx
该项目是一个实验性的 Nintendo Switch Wine 运行时,通过集成 Horizon/libnx 系统服务在 Switch 上加载和运行 AArch64 Windows PE 程序。
Stars: 57 | Forks: 1
# Wine-NX 开关
Wine-NX 是一个专注于 Nintendo Switch 的 Wine 启动项目。其目标是利用 libnx/Horizon 服务来处理进程、内存、输入、文件、显示,并最终实现 GPU 呈现,从而在 Switch NRO 中运行 AArch64 Windows PE 程序。
这个代码库不再仅仅是一个基础的 Horizon 探测程序。它是 Switch Wine 运行时、进程内 Horizon/Wine server 工作、win32u Switch 显示驱动程序以及用于硬件测试的 SD 卡打包程序的工作树。
## 当前状态
截至最新的运行时工作,该项目可以构建并暂存一个 Switch NRO 运行时,用于启动以下来源的 Wine PE 目标:
```
sdmc:/switch/wine
```
记事本是目前主要的真实 GUI 测试目标。它的启动程度已经足以显示主窗口、边框、菜单栏、菜单弹出窗口、文本渲染以及触摸驱动的菜单交互。它仍处于实验阶段,尚不能用于生产环境:响应速度较差,呈现过程极其依赖软件计算,且部分真实应用的子系统仍不完善。
## 目前可行的功能
### Switch 构建与打包
- `wine-nx-probe/build-switch.sh` 使用 `devkitpro/devkita64` Docker 镜像构建 Switch 运行时。
- 该脚本将在以下路径暂存一个可运行的 SD 卡包:
```
wine-nx-probe/build-switch/sd-card/switch/wine
```
- 运行时 NRO 输出:
```
wine-nx-probe/build-switch/wine-nx-runtime.nro
wine-nx-probe/build-switch/sd-card/switch/wine/wine-nx-runtime.nro
```
- `WINE_NX_APP` 选择暂存的目标:
```
WINE_NX_APP=gui ./wine-nx-probe/build-switch.sh
WINE_NX_APP=notepad ./wine-nx-probe/build-switch.sh
```
- 该包现在会将 Wine NLS 文件和 Wine 字体暂存到 Switch 包中,包括等效于 `C:\windows\fonts` 和 `share/wine/fonts` 的目录。
### 运行时加载器
- 运行时可以从暂存的包中引导 Wine ARM64 PE 目标。
- 冒烟测试应用和记事本路径已具备 PE 导入解析、加载器交接、Wine DLL 暂存以及运行时目标选择功能。
- 运行时在 NRO 中携带了一个构建标记,以便硬件日志能够确认实际运行的是哪个二进制文件。
- 日志输出至:
```
sdmc:/switch/wine/wine-nx-runtime.log
sdmc:/switch/wine/horizon-trace.log
```
### Horizon 和 Wine Server 底层支持
- 早期的 Horizon 基础元件已得到验证:TLS 模型、可执行内存/JIT 别名处理、线程设置、地址空间预留以及 Switch 安全的运行时日志记录。
- 进程内 Horizon/Wine server 路径已足够活跃,可以服务于当前的 NTDLL 和 USER/GDI 流程。
- Win32 syscall 调度可用于已执行的 `win32u` 调用。
- USER server 工作的覆盖率已满足 GUI 路径需求:
- atoms;
- window station 和 desktop 基础;
- 类的创建/注册;
- 顶级和弹出窗口对象;
- 足以满足当前窗口需求的共享会话样式定位器/数据。
### GDI 和 USER 启动
- GDI 软件渲染正通过 Wine 的 DIB 路径运行。
- GUI 冒烟测试应用验证了 DC 创建、兼容 DC、DIB section、bitmap、StretchDIBits 风格的光栅操作、对象删除以及光标/菜单绘制。
- `RegisterClassW` 和 `CreateWindow` 已跨越最初的 server 对象阻碍。
- 基本的记事本非客户区渲染正常工作:
- 主边框;
- 标题/操作按钮;
- 菜单栏;
- 菜单弹出表面;
- 通过 FreeType 支持的 Wine 字体渲染的文本。
### Switch 显示驱动
- `dlls/win32u/winnx_drv.c` 是特定于 Switch 的 win32u 显示驱动程序。
- 它向 Wine 报告有一个 1280x720 的主显示器。
- Wine 窗口表面渲染到普通的软件 DIB 内存中,然后驱动程序将脏像素 blit 到 libnx framebuffer。
- 添加了弹出窗口/菜单表面恢复功能,以便在弹出窗口隐藏或移动时可以再次覆盖旧的菜单像素。
- 脏区域呈现工作避免了部分全表面刷新。
- 批量呈现工作保持一个待处理的 framebuffer 处于开启状态,并在更高层次的边界进行呈现,而不是将每个微小的脏矩形都加入队列。
### 输入
- Switch 触摸屏输入通过 libnx 进行采样。
- 触摸被转换为绝对的 Win32 鼠标输入。
- 非客户区命中测试工作良好,足以与记事本边框和顶部菜单进行交互。
- 添加了 `get_window_children_from_point` 支持,以实现正确的子窗口命中路由。
### 真实 GUI 里程碑
当前的真实应用里程碑是 Wine 记事本:
- 窗口在 Switch 上显示;
- 边框/控制图标渲染正确;
- 菜单字体正常渲染;
- 通过触摸可以部分进行顶部菜单交互;
- 可以绘制并消除弹出窗口;
- 应用程序明显处于活动状态,但速度较慢,尚不能提供舒适的 UI 体验。
## 当前问题
### 呈现速度依然过慢
CPU 仍有余量,这充分表明瓶颈在于呈现和同步,而不是原始的 CPU 光栅化。
当前的 libnx Framebuffer 路径使用 `framebufferMakeLinear()`。这使得驱动程序易于编写,但每次调用 `framebufferEnd()` 时,都会将完整的 1280x720 阴影缓冲区转换为 block-linear 布局,然后再将其加入 Switch compositor 的队列。即使 Wine 仅更改了一个小的菜单高亮显示,呈现路径的开销依然可能很高。
目前的批量补丁减少了这种情况发生的频率,但真正的性能提升在于 GPU 呈现路径。由于此代码树已经具有可用的 Mesa/NVK Vulkan 移植,因此首选方向是 Vulkan/NVK compositor:将 Wine DIB/窗口表面上传到 GPU 纹理中,进行合成,并每帧呈现一次。
### UI 完整性仍处于早期阶段
已知的待完善领域:
- 菜单功能正常,但尚未完全达到完美的 Windows 效果;
- 弹出窗口/菜单的 z-order 和失效处理仍需更多的 compositor 逻辑;
- 触摸输入需要更好的捕获、焦点、拖拽和双击行为;
- 尚无手柄/键盘文本输入路径;
- 尚无真正的 GPU 加速;
- 记事本作为一个里程碑是有效的,但这并不能证明任意的 GUI 应用程序都已准备就绪。
### Wine 子系统差距
真实的应用程序需要在以下方面做更多工作:
- USER 消息队列和窗口状态的边缘情况;
- 通用控件和对话框;
- shell/comdlg/shell32 行为;
- 许多应用程序使用的 COM/OLE 路径;
- 注册表和 prefix 行为;
- 超出暂存的 Wine 字体集的字体发现/回退;
- 剪贴板、IME、键盘和手柄输入;
- 音频、网络和多进程行为作为后续里程碑。
## 构建
### 宿主机系统设置
Switch NRO 构建在使用 `devkitpro/devkita64` 镜像的 Docker 内运行。
如有需要,该脚本会在该容器内安装缺失的 Switch portlibs,包括用于字体渲染的 FreeType/Harfbuzz。
宿主机端的 Wine PE 重建路径使用 LLVM-MinGW。默认的预期路径会被 git 忽略:
```
wine-nx-probe/toolchains/llvm-mingw-20260505-ucrt-macos-universal
```
如果 `wine-nx-probe/toolchains/` 缺失,请从指定的 LLVM-MinGW 发行版恢复它:
```
mkdir -p wine-nx-probe/toolchains
curl -L -o wine-nx-probe/toolchains/llvm-mingw-20260505-ucrt-macos-universal.tar.xz \
https://github.com/mstorsjo/llvm-mingw/releases/download/20260505/llvm-mingw-20260505-ucrt-macos-universal.tar.xz
tar -C wine-nx-probe/toolchains \
-xf wine-nx-probe/toolchains/llvm-mingw-20260505-ucrt-macos-universal.tar.xz
```
对于其他宿主机/工具链布局,请将构建指向提取后的 LLVM-MinGW 目录:
```
LLVM_MINGW_DIR=/path/to/llvm-mingw WINE_NX_APP=notepad ./wine-nx-probe/build-switch.sh
```
ARM64 PE Wine 构建树也会被 git 忽略:
```
wine-nx-probe/build-wine-arm64-pe-clean
wine-nx-probe/build-wine-arm64-pe-local
```
这些目录是用于重建记事本和 Wine PE DLL 的一次性 Wine PE 构建树。`wine-nx-probe/build-wine-arm64-pe-clean` 是默认的 PE 构建目录。当 `WINE_NX_APP=notepad` 且 `wine-nx-probe/build-wine-arm64-pe-local` 存在时,`build-switch.sh` 会优先使用本地树作为暂存程序。使用以下命令覆盖任一路径:
```
WINE_NX_PROGRAM_BUILD_DIR=/path/to/wine-pe-build WINE_NX_APP=notepad ./wine-nx-probe/build-switch.sh
```
从代码库根目录重新创建 PE 构建树:
```
PE_BUILD_DIR=wine-nx-probe/build-wine-arm64-pe-clean
mkdir -p "$PE_BUILD_DIR"
(
cd "$PE_BUILD_DIR"
PATH="$PWD/../toolchains/llvm-mingw-20260505-ucrt-macos-universal/bin:$PATH" \
../../configure \
--enable-archs=aarch64 \
--disable-tests \
--without-x \
--without-freetype \
--without-fontconfig \
--without-gettext \
--without-gnutls \
--without-opengl \
--without-vulkan \
--without-sdl \
--without-cups \
--without-coreaudio \
--without-alsa \
--without-pulse \
--without-gstreamer \
--without-ffmpeg \
--without-dbus \
--without-gphoto \
--without-gssapi \
--without-krb5 \
--without-netapi \
--without-opencl \
--without-pcap \
--without-pcsclite \
--without-sane \
--without-usb \
--without-v4l2 \
--without-wayland \
--without-unwind \
--with-mingw=llvm-mingw
)
```
在 configure 之后,正常的 Switch 包构建将从该树中编译缺失的记事本可执行文件和 DLL。
从代码库根目录运行:
```
WINE_NX_APP=notepad ./wine-nx-probe/build-switch.sh
```
其他有用的目标:
```
WINE_NX_APP=gui ./wine-nx-probe/build-switch.sh
WINE_NX_APP=curl ./wine-nx-probe/build-switch.sh
```
暂存的 SD 卡包写入至:
```
wine-nx-probe/build-switch/sd-card/switch/wine
```
将该包复制或同步至:
```
sdmc:/switch/wine
```
包助手会打印:
```
Sync mounted SD with: wine-nx-probe/tools/sync-switch-wine-package.sh
```
## 硬件测试检查清单
1. 使用所需目标构建包,通常是:
```
WINE_NX_APP=notepad ./wine-nx-probe/build-switch.sh
```
2. 部署 `wine-nx-runtime.nro` 和暂存的 `switch/wine` 包。
3. 在 Switch 上运行。
4. 确认运行时日志以预期的标记开头,例如:
```
[BUILD] nx-batched-present-1
```
5. 检查:
```
sdmc:/switch/wine/wine-nx-runtime.log
sdmc:/switch/wine/horizon-trace.log
```
这个标记很重要。在日志显示仍在运行较旧的 NRO 之前,几个性能/调试循环看起来非常令人困惑。
## 重要文件
运行时和打包:
```
wine-nx-probe/build-switch.sh
wine-nx-probe/source/runtime.c
wine-nx-probe/source/runtime_platform.c
wine-nx-probe/CMakeLists.txt
```
Switch 显示和 USER/GDI 集成:
```
dlls/win32u/winnx_drv.c
dlls/win32u/window.c
dlls/win32u/dce.c
dlls/win32u/sysparams.c
dlls/win32u/message.c
dlls/win32u/input.c
```
Horizon/NTDLL 底层支持:
```
dlls/ntdll/unix/horizon.c
dlls/ntdll/unix/file.c
dlls/ntdll/unix/process.c
dlls/ntdll/unix/thread.c
dlls/ntdll/unix/signal_arm64.c
```
冒烟测试目标:
```
wine-nx-probe/samples/gui-smoke
wine-nx-probe/samples/curl-arm64
```
## 下一阶段里程碑
1. 呈现性能
替换或绕过高开销的线性 framebuffer 路径。首选方向:
- 用于 Wine 软件窗口表面的 Vulkan/NVK compositor;
- 将 DIB/窗口表面上传到 GPU 纹理中;
- 在 GPU 上进行弹出窗口/窗口合成;
- 每帧呈现一次;
- 待 Wine 的 Vulkan 路径足够完善,能够进行真正的 D3D 到 Vulkan 加速后,再支持 DXVK/vkd3d。
备选或底层方案:
- deko3d compositor;
- 直接进行 block-linear 脏区域转换;
- 持久的软件后备存储加上每帧呈现一次。
2. 输入优化
- 键盘/文本输入;
- 手柄鼠标模式;
- 更好的触摸捕获和焦点处理;
- 双击/拖拽行为;
- 原生 `WM_TOUCH` 支持(稍后进行)。
3. USER/窗口管理器行为
- 弹出窗口/菜单堆叠;
- 所有者/激活边缘情况;
- 裁剪和失效处理;
- 模态对话框;
- 子窗口排序。
4. 更多真实应用程序
在记事本运行更流畅之后,下一个有用的测试目标应该是小型的非网络 GUI 应用程序,它们能够测试对话框、通用控件、编辑控件和文件浏览,而无需首先依赖浏览器引擎或 GPU API。
标签:AArch64, Bash脚本, libnx, Wine, 任天堂Switch, 兼容层, 客户端加密, 系统移植, 请求拦截