Skip to content

feat: support building for Windows on ARM (aarch64) via MSYS2 CLANGARM64 - #1811

Open
sci42 wants to merge 6 commits into
flxzt:mainfrom
sci42:win-arm64
Open

feat: support building for Windows on ARM (aarch64) via MSYS2 CLANGARM64#1811
sci42 wants to merge 6 commits into
flxzt:mainfrom
sci42:win-arm64

Conversation

@sci42

@sci42 sci42 commented Jul 23, 2026

Copy link
Copy Markdown

Makes the Windows build work in the MSYS2 CLANGARM64 environment, so Rnote runs
natively on Windows on ARM devices, with GPU rendering.

Approach

Rather than adding aarch64 branches, the Windows recipes now take the
architecture-dependent parts from the MSYS2 environment itself:

  • package prefix from $MINGW_PACKAGE_PREFIX
  • install prefix from $MSYSTEM_PREFIX (via cygpath -m), replacing the
    hardcoded C:/msys64/mingw64
  • Inno-Setup ArchitecturesAllowed from host_machine.cpu_family()
  • openssl DLLs by glob, since the filename carries the architecture
    (libcrypto-3-x64.dll vs libcrypto-3-arm64.dll)
    Separately, inno_build.py no longer uses ldd to find the DLLs to package.
    ldd resolves imports by loading the binary, and ANGLE's libGLESv2.dll blocks
    while loading, which made the installer build hang indefinitely. It now reads the
    PE import table with objdump -p and walks it transitively; the resulting set is
    verified to be closed. That commit is independent of ARM and can be taken on its
    own.

The existing MINGW64 workarounds are unchanged and now sit behind an explicit
MSYSTEM == MINGW64 check, so the x86_64 path keeps its exact previous behaviour.

CLANGARM64 needs the gtk/libadwaita/gettext pinning for the same reasons MINGW64
does, but not the libpthread.dll.a rename — linking with lld works as is.

New

  • just setup-win-dev — devel build installing into $MSYSTEM_PREFIX instead of
    /usr, so the app can be run locally without building the installer.
  • misc/building/rnote-windows-arm64-build.md

Tested

On a Surface Pro 11 (Snapdragon X Elite X1E80100, Adreno X1-85, Windows 11):

  • rnote-cli test misc/file-tests/*.rnote passes for all six fixtures
  • GSK_DEBUG=renderer reports Using renderer 'GskGLRenderer' on ANGLE, drawing
    is smooth and the rendering artifacts seen under the cairo fallback are gone
  • installer builds, installs, and the installed app is stable across ten minutes
    of ordinary use (drawing, settings, zooming) — the same usage crashed the app
    after roughly two minutes before the GDK_DISABLE=wgl change
  • opening a document containing a text stroke: 3 of 3 crashes before the
    PANGOCAIRO_BACKEND=fc change, 5 of 5 clean after, with the crash reproducing
    identically on the meson-installed build and on the installed app from the
    installer. Forcing PANGOCAIRO_BACKEND=win32 back on brings the crash back,
    which also confirms the environment still overrides the default

Not tested by me:

  • The MINGW64/x86_64 path. It should be behaviourally unchanged, but it deserves
    a CI run or a second pair of eyes.
  • Whether the DirectWrite font path is equally broken on x86_64 Windows. No
    hardware available, which is why both env defaults are scoped to aarch64
    rather than to Windows in general. If it turns out to be broken there too,
    widening the scope is a one-line change.
  • Note also that the PANGOCAIRO_BACKEND default lives in rnote-ui, so
    rnote-cli does not get it. CLI export worked with either backend in my
    tests, but I did not investigate why it is unaffected.

Disclosure per CONTRIBUTING.md: this was implemented with Claude Code
(Anthropic). No Co-authored-by:/Assisted-by: trailers were added to the
commits, per the same document.

sci42 and others added 6 commits July 22, 2026 02:44
Take the architecture dependent parts of the Windows build from the MSYS2
environment instead of hardcoding x86_64/mingw64, so the same recipes serve
MINGW64 and CLANGARM64:

- package prefix from $MINGW_PACKAGE_PREFIX
- install prefix from $MSYSTEM_PREFIX, replacing the hardcoded C:/msys64/mingw64
- Inno-Setup ArchitecturesAllowed from host_machine.cpu_family()
- openssl DLLs by glob, since the file name carries the architecture
  (libcrypto-3-x64.dll vs libcrypto-3-arm64.dll)
- the ldd filter in inno_build.py from the environment name, so it matches
  /clangarm64/ as well as /mingw64/

The existing MINGW64 workarounds are unchanged and now sit behind an explicit
MSYSTEM check, so the x86_64 path keeps its previous behaviour.

CLANGARM64 needs the same gtk/libadwaita/gettext pinning as MINGW64, for the
same reasons: without it GSK cannot realize GL or Vulkan on a GdkWin32Toplevel
and silently falls back to software rendering, and those older gtk/libadwaita
builds import DllMain from libintl-8.dll. It does not need the libpthread.dll.a
rename, linking with lld works as is.

Also adds `just setup-win-dev`, a devel build that installs into $MSYSTEM_PREFIX
instead of /usr, so the app can be run locally without building the installer.
`ldd` resolves imports by actually loading the binary. ANGLE's libGLESv2.dll
blocks while being loaded, so `meson compile build-installer` hangs there
indefinitely - no error, no CPU usage, which reads as a slow build rather than a
deadlock.

Read the PE import table with `objdump -p` and walk it transitively instead,
which executes nothing. DLLs that are not present in the build environment are
Windows system DLLs and stay unpackaged, matching the previous filter. The
collected set is verified to be closed: nothing reachable from the binaries or
the gdk-pixbuf loaders is missing.

Whether the old code hangs on x86_64 depends only on how that ANGLE build
behaves at load time, so this is not specific to aarch64.
Windows on ARM ships no native desktop OpenGL driver, so WGL is serviced by
Microsoft's OpenGLOn12.dll, which translates OpenGL onto Direct3D 12. GTK picks
that path by default and reports "Renderer: D3D12 (Qualcomm(R) Adreno(TM) X1-85
GPU)".

That layer crashes: after a few minutes of ordinary drawing and changing
settings the process dies with an access violation and the event log points at
OpenGLOn12.dll (0xc0000005).

Default GDK_DISABLE to "wgl" on aarch64, which makes GDK pick EGL and with it
the ANGLE already shipped alongside the app, mapping GL ES onto Direct3D 11:

     - Vendor: Google Inc. (Qualcomm)
     - Version: 1.5 (ANGLE 2.1.25748)
    Using renderer 'GskGLRenderer'

GPU rendering is fully preserved and the crashes are gone, verified over ten
minutes of normal use where the WGL path died after about two.

Set only as a default so GDK_DISABLE from the environment still wins, and scoped
to aarch64 because x86_64 has real OpenGL drivers where ANGLE would be a detour
rather than a fix.
pangocairo renders glyphs through cairo's win32 font backend, which goes to
DirectWrite and Direct2D. On Windows on ARM that path is broken: pango logs
"All font fallbacks failed" once per layout (219 times during a single startup)
and rendering text dies with an access violation in d2d1.dll (0xc0000005).

It reliably takes the app down when a document containing a text stroke is
opened, or when the typewriter is used. Opening
misc/file-tests/v0-15-0-test.rnote crashed 3 of 3 runs.

This is not a missing font: fontconfig sees 724 fonts in the same environment
and fc-match resolves correctly, and patching a document's font_family from
"serif" to "Noto Serif" changes nothing. Only the DirectWrite path fails.

Default PANGOCAIRO_BACKEND to "fc", which selects the fontconfig/freetype
backend the Linux builds use anyway. The same document then opens cleanly in
5 of 5 runs with no font warnings, and text still renders correctly - verified
by exporting to PNG and comparing.

Note that std::env::set_var does not work for this, unlike for the GDK_DISABLE
default above. On Windows it only calls SetEnvironmentVariableW, which updates
the Win32 environment block; GDK reads its variables with g_getenv and sees
that, but pangocairo uses plain getenv, which returns the copy the C runtime
builds at startup. _putenv_s updates that copy, and libpangocairo resolves to
the same UCRT as the app.

Set only as a default so PANGOCAIRO_BACKEND from the environment still wins,
and scoped to aarch64 because x86_64 was not tested.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant