← back

run windows apps in termux but natively no root or proot

Getting a full copy of Adobe Photoshop running on an Android phone sounds like a party trick, but with Hangover (a Wine fork that adds ARM64 support via box64/FEX-emu translation) and a couple of small wrapper scripts, it’s a genuinely usable setup inside Termux. This post walks through what worked, what didn’t, and the scripts I use to keep things sane.

TL;DR results

  • Photoshop CC 2020 — runs.
  • Photoshop CS2015 — runs just as well as CC 2017.
  • Photoshop CC 2017 — runs.
  • Everything above is running as x86 (32-bit) only. x64 builds refuse to cooperate.

Why x86 only

Hangover gives you a choice of translation backends for both the x86 and x64 sides of WoW64 (FEX-emu, box64, or the native CPU path), but in practice only the 32-bit Photoshop builds come up cleanly for me. The x64 builds either hang during Adobe’s installer or crash the moment the UI tries to initialize. Rather than fight it, I just standardized on installing the “(32 Bit)” Photoshop variant across every version I tried, which sidesteps the issue entirely.

DXVK dll headaches

Hangover wine repo provide DXVK dlls. You can place them in syswow64 and system32 directories and over ride builtin. But for me they were messing things more even I was using vulkan-mesa-icd-freedeno drivers.

What Wine actually needs

Through trial and error, the winetricks verbs that matter for a working prefix are:

  • gdiplus
  • win7 (Windows version override)
  • corefonts
  • fontsmooth=rgb

Skip any of these and you’ll see missing UI glyphs, broken dialogs, or font rendering that makes panels unreadable.

The vcrun crash trap

Manually installing vcrun2015 and vcrun2019 through winetricks reliably crashed the prefix for me. The fix was to not install them myself at all — the Photoshop installer bundles and installs the VC++ redistributables it needs as part of its own setup routine. Let Adobe’s installer do that work instead of winetricks.

The setup script

Here’s the script I use to wire everything together: it symlinks the wine binary to Hangover’s build, drops in a wineserver shim, fetches winetricks, creates a desktop launcher for Photoshop, and builds two families of wrapper commands (hw* and wt*) so I can switch translation backends per-launch without editing environment variables by hand every time.

#!/data/data/com.termux/files/usr/bin/env sh

echo "creating wine link to hangover-wine"
ln -sf /data/data/com.termux/files/usr/bin/hangover-wine /data/data/com.termux/files/usr/bin/wine

echo "writing wineserver wrapper"
cat << 'EOF' > "$BIN_DIR/wineserver"
#!/data/data/com.termux/files/usr/bin/env sh
exec /data/data/com.termux/files/usr/opt/hangover-wine/bin/wineserver "$@"
EOF

echo "Downloading winetricks"
wget -O "$BIN_DIR/winetricks" \
    https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks
chmod +x "$BIN_DIR/winetricks"

echo "Creating desktop file"
mkdir -p "$APP_DIR"
cat > "$APP_DIR/photoshop.desktop" << 'EOF'
[Desktop Entry]
Version=1.0
Type=Application
Name=Adobe Photoshop
Comment=Launch Adobe Photoshop via Wine
Exec=env HODLL=libwow64fex.dll wine "C:\\Program Files (x86)\\Adobe\\Adobe Photoshop\\Photoshop.exe"
Icon=photoshop
Categories=Graphics;
Path=
Terminal=false
StartupNotify=false
EOF

cat > "$BIN_DIR/hwrap" << 'EOF'
#!/data/data/com.termux/files/usr/bin/bash
#
cmd="$(basename "$0")"
#
case "$cmd" in
    hwfex)
        # FEX for both x64 and x86
        export HODLL64="libarm64ecfex.dll"
        export HODLL="libwow64fex.dll"
        ;;
    hw64fex)
        # FEX for x64 only
        export HODLL64="libarm64ecfex.dll"
        ;;
    hw32fex)
        # FEX for x86 only
        export HODLL="libwow64fex.dll"
        ;;
    hwcpu)
        # Native WoW64 CPU for x86
        export HODLL="wow64cpu.dll"
        ;;
    hwbox)
        # Box64-based x86 emulation
        export HODLL="wowbox64.dll"
        ;;
    *)
        echo "Unknown invocation: $cmd"

        echo "Usage examples:"
        echo "hwfex   for both x64 and x32"
        echo "hw64fex for x64 and fex"
        echo "hw32fex for x32 and fex"
        echo "hwcpu   for x32 and native"
        echo "hwbox   for x32 and box64"
        exit 1
        ;;
esac

exec wine "$@"
EOF

cat > "$BIN_DIR/wtwrap" << 'EOF'
#!/data/data/com.termux/files/usr/bin/bash

export WINE="/data/data/com.termux/files/usr/bin/hangover-wine"
export WINESERVER="/data/data/com.termux/files/usr/opt/hangover-wine/bin/wineserver"
export WINEPREFIX="$HOME/.wine"
#
cmd="$(basename "$0")"
#
case "$cmd" in
    wtfex)
        # FEX for both x64 and x86
        export HODLL64="libarm64ecfex.dll"
        export HODLL="libwow64fex.dll"
        ;;
    wt64fex)
        # FEX for x64 only
        export HODLL64="libarm64ecfex.dll"
        ;;
    wt32fex)
        # FEX for x86 only
        export HODLL="libwow64fex.dll"
        ;;
    wtcpu)
        # Native WoW64 CPU for x86
        export HODLL="wow64cpu.dll"
        ;;
    wtbox)
        # Box64-based x86 emulation
        export HODLL="wowbox64.dll"
        ;;
    *)
        echo "Unknown invocation: $cmd"

        echo "Usage examples:"
        echo "wtfex   for both x64 and x32"
        echo "wt64fex for x64 and fex"
        echo "wt32fex for x32 and fex"
        echo "wtcpu   for x32 and native"
        echo "wtbox   for x32 and box64"
        exit 1
        ;;
esac

exec winetricks "$@"
EOF

chmod +x "$BIN_DIR/hwrap"
chmod +x "$BIN_DIR/wtwrap"

echo "Creating links"
ln -sf "$BIN_DIR/hwrap" "$BIN_DIR/hwfex"
ln -sf "$BIN_DIR/hwrap" "$BIN_DIR/hw64fex"
ln -sf "$BIN_DIR/hwrap" "$BIN_DIR/hw32fex"
ln -sf "$BIN_DIR/hwrap" "$BIN_DIR/hwcpu"
ln -sf "$BIN_DIR/hwrap" "$BIN_DIR/hwbox"

ln -sf "$BIN_DIR/wtwrap" "$BIN_DIR/wtfex"
ln -sf "$BIN_DIR/wtwrap" "$BIN_DIR/wt64fex"
ln -sf "$BIN_DIR/wtwrap" "$BIN_DIR/wt32fex"
ln -sf "$BIN_DIR/wtwrap" "$BIN_DIR/wtcpu"
ln -sf "$BIN_DIR/wtwrap" "$BIN_DIR/wtbox"

How the wrappers work

Both wrapper families use $(basename "$0") to figure out which symlink was called, then set the right HODLL/HODLL64 environment variables before handing off to the real binary:

  • hw* commands (hwfex, hw64fex, hw32fex, hwcpu, hwbox) wrap wine directly — use these for actually launching Photoshop with a specific translation backend.
  • wt* commands (wtfex, wt64fex, wt32fex, wtcpu, wtbox) wrap winetricks with the same backend choices, plus set WINE, WINESERVER, and WINEPREFIX explicitly, since winetricks needs those to find the right prefix.

The five backend choices per family:

Suffix Backend
fex FEX-emu for both x64 and x86
64fex FEX-emu for x64 only
32fex FEX-emu for x86 only
cpu Native WoW64 CPU path (x86)
box box64-based emulation (x86)

Since everything Photoshop-related here is x86-only for me, in practice hw32fex and wt32fex are what I actually reach for day to day; the rest exist mostly so I can quickly A/B test whether a different backend fixes a given crash.

Putting it together

  1. Run the setup script once to create the symlinks, wrapper commands, and desktop launcher.
  2. Set up a WINEPREFIX, then run the font/UI prerequisites through the wrapper: wt32fex corefonts gdiplus win7 fontsmooth=rgb.
  3. Run the Photoshop installer itself through hw32fex — let it install its own bundled VC++ redistributables rather than pre-installing vcrun2015/vcrun2019 separately.
  4. Launch via the photoshop.desktop entry, or directly with hw32fex "C:\Program Files (x86)\Adobe\...\Photoshop.exe".

That’s the whole recipe. It’s not perfect — not everything works in photoshop while major headache is generator, x64 is a no-go, and you’ll want patience during the Adobe installer — but for basic photo editing on a phone, it’s a surprisingly solid setup given mobile photoshop app is battery hungry and no alignments.


← back