Wine-NX is a Nintendo Switch focused Wine bring-up. The goal is to run AArch64 Windows PE programs inside a Switch NRO, using libnx/Horizon services for process, memory, input, files, display, and eventually GPU presentation.
This repository is no longer just a primitive Horizon probe. It is the working tree for the Switch Wine runtime, the in-process Horizon/Wine server work, the win32u Switch display driver, and the SD-card package builder used for hardware testing.
As of the latest runtime work, the project can build and stage a Switch NRO runtime that launches Wine PE targets from:
sdmc:/switch/wine
Notepad is the main real-GUI test target right now. It can launch far enough to show the main window, frame, menu bar, menu popups, text rendering, and touch driven menu interaction. It is still experimental and not production usable: responsiveness is poor, presentation is software-heavy, and several real-app subsystems remain incomplete.
wine-nx-probe/build-switch.shbuilds the Switch runtime with thedevkitpro/devkita64Docker image.- The script stages a runnable SD package under:
wine-nx-probe/build-switch/sd-card/switch/wine
- Runtime NRO output:
wine-nx-probe/build-switch/wine-nx-runtime.nro
wine-nx-probe/build-switch/sd-card/switch/wine/wine-nx-runtime.nro
WINE_NX_APPselects the staged target:
WINE_NX_APP=gui ./wine-nx-probe/build-switch.sh
WINE_NX_APP=notepad ./wine-nx-probe/build-switch.sh- The package now stages Wine NLS files and Wine fonts into the Switch package,
including
C:\windows\fontsandshare/wine/fontsequivalents.
- The runtime can bootstrap a Wine ARM64 PE target from the staged package.
- PE import resolution, loader handoff, Wine DLL staging, and runtime target selection are in place for the smoke apps and Notepad path.
- The runtime carries a build marker in the NRO so hardware logs can confirm which binary is actually running.
- Logging goes to:
sdmc:/switch/wine/wine-nx-runtime.log
sdmc:/switch/wine/horizon-trace.log
- Early Horizon primitives were proven: TLS model, executable memory/JIT alias handling, thread setup, address-space reservation, and Switch-safe runtime logging.
- The in-process Horizon/Wine server path is active enough to service the current NTDLL and USER/GDI flows.
- Win32 syscall dispatch is working for the exercised
win32ucalls. - USER server work has enough coverage for the GUI path:
- atoms;
- window station and desktop basics;
- class creation/registration;
- top-level and popup window objects;
- shared-session style locators/data sufficient for current windows.
- GDI software rendering is working through Wine's DIB path.
- The GUI smoke app proved DC creation, compatible DCs, DIB sections, bitmaps, StretchDIBits-style raster work, object deletion, and cursor/menu drawing.
RegisterClassWandCreateWindowmoved past the original server-object blockers.- Basic Notepad non-client rendering works:
- main frame;
- title/action buttons;
- menu bar;
- menu popup surfaces;
- text through FreeType-backed Wine fonts.
dlls/win32u/winnx_drv.cis the Switch-specific win32u display driver.- It reports a single 1280x720 primary monitor to Wine.
- Wine window surfaces render into normal software DIB memory, then the driver blits dirty pixels to the libnx framebuffer.
- Popup/menu surface restore was added so old menu pixels can be covered again when popups hide or move.
- Dirty-present work avoids some full-surface flushes.
- Batched-present work keeps a pending framebuffer open and presents at higher-level boundaries instead of queueing every tiny dirty rectangle.
- Switch touchscreen input is sampled through libnx.
- Touch is translated into absolute Win32 mouse input.
- Non-client hit tests work well enough to interact with the Notepad frame and top menus.
get_window_children_from_pointsupport was added for correct child-window hit routing.
The current real-app milestone is Wine Notepad:
- window appears on Switch;
- frame/control glyphs render correctly;
- menu fonts render;
- top menu interaction is partially working via touch;
- popups can be drawn and dismissed;
- the app is visibly alive, but slow and not yet a comfortable UI.
CPU headroom remains, which strongly suggests the bottleneck is presentation and synchronization, not raw CPU rasterization.
The current libnx Framebuffer path uses framebufferMakeLinear(). That makes
the driver easy to write, but every framebufferEnd() converts the full
1280x720 shadow buffer into block-linear layout before queueing it to the
Switch compositor. Even if Wine only changes a small menu highlight, the
present path can still become expensive.
The current batching patch reduces how often this happens, but the real performance step is a GPU presentation path. Since this tree already has a Mesa/NVK Vulkan port available, the preferred direction is a Vulkan/NVK compositor: upload Wine DIB/window surfaces into GPU textures, composite them, and present once per frame.
Known rough areas:
- menus are functional but not fully Windows-perfect;
- popup/menu z-order and invalidation still need more compositor logic;
- touch input needs better capture, focus, drag, and double-click behavior;
- no controller/keyboard text-input path yet;
- no real GPU acceleration yet;
- Notepad works as a milestone, not as proof that arbitrary GUI apps are ready.
Real applications will need more work in:
- USER message queue and window state edge cases;
- common controls and dialogs;
- shell/comdlg/shell32 behavior;
- COM/OLE paths used by many apps;
- registry and prefix behavior;
- font discovery/fallback beyond the staged Wine font set;
- clipboard, IME, keyboard, and controller input;
- audio, networking, and multi-process behavior as later milestones.
The Switch NRO build runs inside Docker with the devkitpro/devkita64 image.
The script installs missing Switch portlibs inside that container when needed,
including FreeType/Harfbuzz for font rendering.
The host-side Wine PE rebuild path uses LLVM-MinGW. The default expected path is ignored by git:
wine-nx-probe/toolchains/llvm-mingw-20260505-ucrt-macos-universal
If wine-nx-probe/toolchains/ is missing, restore it from the pinned
LLVM-MinGW release:
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.xzFor another host/toolchain layout, point the build at an extracted LLVM-MinGW directory:
LLVM_MINGW_DIR=/path/to/llvm-mingw WINE_NX_APP=notepad ./wine-nx-probe/build-switch.shThe ARM64 PE Wine build trees are also ignored by git:
wine-nx-probe/build-wine-arm64-pe-clean
wine-nx-probe/build-wine-arm64-pe-local
Those directories are disposable Wine PE build trees used to rebuild Notepad
and Wine PE DLLs. wine-nx-probe/build-wine-arm64-pe-clean is the default PE
build directory. When WINE_NX_APP=notepad and
wine-nx-probe/build-wine-arm64-pe-local exists, build-switch.sh prefers the
local tree for the staged program. Override either path with:
WINE_NX_PROGRAM_BUILD_DIR=/path/to/wine-pe-build WINE_NX_APP=notepad ./wine-nx-probe/build-switch.shTo recreate a PE build tree from the repository root:
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
)After configure, the normal Switch package build will compile the missing Notepad executable and DLLs from that tree.
From the repository root:
WINE_NX_APP=notepad ./wine-nx-probe/build-switch.shOther useful targets:
WINE_NX_APP=gui ./wine-nx-probe/build-switch.sh
WINE_NX_APP=curl ./wine-nx-probe/build-switch.shThe staged SD package is written to:
wine-nx-probe/build-switch/sd-card/switch/wine
Copy or sync that package to:
sdmc:/switch/wine
The package helper prints:
Sync mounted SD with: wine-nx-probe/tools/sync-switch-wine-package.sh
- Build the package with the target you want, usually:
WINE_NX_APP=notepad ./wine-nx-probe/build-switch.sh-
Deploy
wine-nx-runtime.nroand the stagedswitch/winepackage. -
Run on Switch.
-
Confirm the runtime log starts with the expected marker, for example:
[BUILD] nx-batched-present-1
- Check:
sdmc:/switch/wine/wine-nx-runtime.log
sdmc:/switch/wine/horizon-trace.log
The marker matters. Several performance/debug loops looked confusing until the logs showed an older NRO was still being run.
Runtime and packaging:
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 display and USER/GDI integration:
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 substrate:
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
Smoke targets:
wine-nx-probe/samples/gui-smoke
wine-nx-probe/samples/curl-arm64
- Presentation performance
Replace or bypass the expensive linear framebuffer path. Preferred direction:
- Vulkan/NVK compositor for Wine software window surfaces;
- DIB/window surface upload into GPU textures;
- popup/window composition on GPU;
- one present per frame;
- DXVK/vkd3d later, after Wine's Vulkan path is clean enough for real D3D-to-Vulkan acceleration.
Fallback or lower-level options:
- deko3d compositor;
- direct block-linear dirty conversion;
- persistent software backing store plus one present per frame.
- Input polish
- keyboard/text input;
- controller mouse mode;
- better touch capture and focus;
- double-click/drag behavior;
- native
WM_TOUCHlater.
- USER/window manager behavior
- popup/menu stacking;
- owner/activation edge cases;
- clipping and invalidation;
- modal dialogs;
- child-window ordering.
- More real apps
After Notepad is smoother, the next useful test targets should be small non-network GUI apps that exercise dialogs, common controls, edit controls, and file browsing without requiring a browser engine or GPU API first.