Skip to content

Instantly share code, notes, and snippets.

@floriankapaun
Last active July 30, 2026 08:12
Show Gist options
  • Select an option

  • Save floriankapaun/15481b5348d65e2d05078cc37bd11a8b to your computer and use it in GitHub Desktop.

Select an option

Save floriankapaun/15481b5348d65e2d05078cc37bd11a8b to your computer and use it in GitHub Desktop.
Run PDS Software Client on MacOS

PDS Software Client on macOS via Wine

The pds Software Client is Windows-only. It runs under Wine on macOS, but two things have to be fixed first or it will fail.

install-pds-wine.sh fixes both and creates a double-clickable app.

Verified on macOS 15 (Apple Silicon), Wine 11.0 (Homebrew), pds client 13.0.8.

Setup

brew install --cask wine-stable          # if you don't have Wine
wine ./path-to/pds_13.0.8.exe            # run the pds installer under Wine
./install-pds-wine.sh https://<your-tenant>.pdscloud.de/pds

Then launch pds Software Client from ~/Applications.

Use your own tenant URL — the same one the Windows install guide tells you to put in the shortcut's properties. The installer's .desktop and .lnk files on the Desktop are useless on macOS; ignore or delete them.

Overridable: WINEPREFIX, APP_DIR, WINE_BIN, APP_NAME. Re-running is safe and idempotent.

What it fixes

1. Fonts. A fresh Wine prefix has an empty drive_c/windows/Fonts. Java's Win32 font manager treats that as fatal — Probable fatal error: No physical fonts found. — and the client exits instantly, showing nothing. The script copies the 34 MS core fonts macOS already ships in /System/Library/Fonts/Supplemental into the prefix under the filenames Java expects (arial.ttf, tahoma.ttf, …). Nothing is downloaded or redistributed.

2. Java2D's Direct3D pipeline. Java2D defaults to Direct3D on Windows. Under Wine that fails via wined3d, and Swing paints into a surface that never reaches the screen. The client logs in fine and the menus work, but any screen you open afterwards is blank and button hover effects never appear. There is no exception anywhere — the UI is fully alive underneath, just invisible (the form will validate your input and refuse to close). Fixed with -Dsun.java2d.d3d=false.

Why _JAVA_OPTIONS and not a command-line flag

pdslauncher.jar is only a bootstrap. It hands off to getdown, which spawns the real client (de.pds.application.client.PdsStandaloneLauncher) as a separate process, with arguments taken from getdown.txt. A -D flag on the javaw.exe command line reaches the bootstrap JVM and stops there — which looks exactly like the flag not working. _JAVA_OPTIONS is read by every HotSpot JVM in the process tree, so it reaches the client.

To check it landed, against the client pid (not the bootstrap):

export WINEPREFIX="$HOME/.wine"
JB="C:\\Program Files (x86)\\PDS\\pds Software Client\\pdsjre\\bin"
wine "$JB\\jps.exe" -l                      # find PdsStandaloneLauncher's pid
wine "$JB\\jcmd.exe" <pid> VM.system_properties | grep java2d

Known limitations

The embedded browser (JxBrowser/Chromium) does not start under Wine. Its child process dies during init — IllegalStateException: Failed to receive the response from initChromium, leaving a ~45 MB dump under AppData/Local/JxBrowser/<version>/CrashReports/. Screens embedding a browser fail, and it can end the ULC session.

Where the logs are

The client redirects its own stdout/stderr, so a terminal shows you nothing useful. Look in ~/.wine/drive_c/users/$USER/polaris/:

file contents
ulc-*.log application errors (ULC client logger, level WARNING)
console-{out,err}-*.log redirected streams — empty if it died early
direct-*.log native Windows message monitor

For a startup crash that logs nothing at all, bypass the redirection entirely with a JVM-level flag: -Xlog:exceptions=info:file=<path>.

#!/bin/zsh
# Make the pds Software Client usable under Wine on macOS.
#
# Run the pds installer under Wine FIRST, then run this. It fixes the two
# things that keep the client from working, and creates a double-clickable app.
#
# ./install-pds-wine.sh https://<your-tenant>.pdscloud.de/pds
#
# Overridable: WINEPREFIX, APP_DIR, WINE_BIN, APP_NAME
set -euo pipefail
PDS_URL="${1:-${PDS_URL:-}}"
WINEPREFIX="${WINEPREFIX:-$HOME/.wine}"
APP_DIR="${APP_DIR:-$HOME/Applications}"
APP_NAME="${APP_NAME:-pds Software Client}"
WINE_BIN="${WINE_BIN:-$(command -v wine || true)}"
PDS_DIR="$WINEPREFIX/drive_c/Program Files (x86)/PDS/pds Software Client"
FONT_SRC="/System/Library/Fonts/Supplemental"
FONT_DST="$WINEPREFIX/drive_c/windows/Fonts"
die() { print -u2 "error: $*"; exit 1; }
[[ -n "$PDS_URL" ]] || die "usage: $0 https://<your-tenant>.pdscloud.de/pds"
[[ -n "$WINE_BIN" ]] || die "wine not found. Install it first (e.g. brew install --cask wine-stable)."
[[ -d "$PDS_DIR" ]] || die "pds client not found at:
$PDS_DIR
Run the pds installer under Wine first: wine ~/Downloads/pds_<version>.exe"
# ---------------------------------------------------------------------------
# 1. Fonts.
#
# A fresh Wine prefix has an EMPTY drive_c/windows/Fonts. Java's Win32 font
# manager treats that as fatal -- "Probable fatal error: No physical fonts
# found." -- and the client exits immediately with nothing on screen and
# nothing in any log. macOS ships the MS core fonts, so we just copy them in
# under the filenames Java's Windows font config expects.
# ---------------------------------------------------------------------------
print "Installing fonts into $FONT_DST"
mkdir -p "$FONT_DST"
n=0
while IFS='|' read -r src dst; do
[[ -z "$src" ]] && continue
if [[ -f "$FONT_SRC/$src" ]]; then
cp -f "$FONT_SRC/$src" "$FONT_DST/$dst"
n=$((n + 1))
fi
done <<'FONTS'
Arial.ttf|arial.ttf
Arial Bold.ttf|arialbd.ttf
Arial Italic.ttf|ariali.ttf
Arial Bold Italic.ttf|arialbi.ttf
Arial Black.ttf|ariblk.ttf
Arial Unicode.ttf|arialuni.ttf
Times New Roman.ttf|times.ttf
Times New Roman Bold.ttf|timesbd.ttf
Times New Roman Italic.ttf|timesi.ttf
Times New Roman Bold Italic.ttf|timesbi.ttf
Courier New.ttf|cour.ttf
Courier New Bold.ttf|courbd.ttf
Courier New Italic.ttf|couri.ttf
Courier New Bold Italic.ttf|courbi.ttf
Tahoma.ttf|tahoma.ttf
Tahoma Bold.ttf|tahomabd.ttf
Verdana.ttf|verdana.ttf
Verdana Bold.ttf|verdanab.ttf
Verdana Italic.ttf|verdanai.ttf
Verdana Bold Italic.ttf|verdanaz.ttf
Georgia.ttf|georgia.ttf
Georgia Bold.ttf|georgiab.ttf
Georgia Italic.ttf|georgiai.ttf
Georgia Bold Italic.ttf|georgiaz.ttf
Trebuchet MS.ttf|trebuc.ttf
Trebuchet MS Bold.ttf|trebucbd.ttf
Trebuchet MS Italic.ttf|trebucit.ttf
Trebuchet MS Bold Italic.ttf|trebucbi.ttf
Comic Sans MS.ttf|comic.ttf
Comic Sans MS Bold.ttf|comicbd.ttf
Andale Mono.ttf|andalemo.ttf
Impact.ttf|impact.ttf
Webdings.ttf|webdings.ttf
Wingdings.ttf|wingding.ttf
FONTS
[[ "$n" -gt 0 ]] || die "no fonts copied -- is $FONT_SRC missing?"
print " $n fonts installed"
# ---------------------------------------------------------------------------
# 2. The launcher app.
#
# Two things matter here:
# * the -jar/URL arguments (the Windows guide tells you to put these in the
# shortcut's properties; on macOS they go here)
# * -Dsun.java2d.d3d=false, which is REQUIRED, not tuning. Java2D defaults
# to Direct3D on Windows; under Wine that fails via wined3d and Swing
# paints into a surface that never reaches the screen. Screens open blank,
# hover effects don't render, and NO exception is logged -- the UI is
# fully alive underneath, just invisible.
#
# It goes in _JAVA_OPTIONS, NOT on the command line. pdslauncher.jar only
# bootstraps: it hands off to getdown, which spawns the real client as a
# separate process with arguments from getdown.txt. A command-line -D
# reaches the bootstrap JVM and stops there -- which looks exactly like
# the flag not working. _JAVA_OPTIONS is read by every JVM in the tree.
# ---------------------------------------------------------------------------
APP="$APP_DIR/$APP_NAME.app"
print "Creating $APP"
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
cat > "$APP/Contents/MacOS/pds" <<LAUNCHER
#!/bin/zsh
export WINEPREFIX="$WINEPREFIX"
PDS_DIR="\$WINEPREFIX/drive_c/Program Files (x86)/PDS/pds Software Client"
cd "\$PDS_DIR" || exit 1
# Must be _JAVA_OPTIONS: getdown spawns the real client as a separate process,
# so command-line -D flags never reach it. See install script for details.
export _JAVA_OPTIONS="-Dsun.java2d.d3d=false -Dsun.java2d.noddraw=true"
exec "$WINE_BIN" "\$PDS_DIR/pdsjre/bin/javaw.exe" \\
-jar pdslauncher.jar "$PDS_URL"
LAUNCHER
chmod +x "$APP/Contents/MacOS/pds"
cat > "$APP/Contents/Info.plist" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key><string>$APP_NAME</string>
<key>CFBundleIdentifier</key><string>de.pds.softwareclient.winewrapper</string>
<key>CFBundleExecutable</key><string>pds</string>
<key>CFBundleIconFile</key><string>pds</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>NSHighResolutionCapable</key><true/>
</dict>
</plist>
PLIST
# Icon, best effort -- purely cosmetic, so nothing here may abort the script.
tmp="$(mktemp -d 2>/dev/null || true)"
if [[ -f "$PDS_DIR/pds.ico" && -n "$tmp" && -d "$tmp" ]]; then
if sips -s format png "$PDS_DIR/pds.ico" --out "$tmp/pds.png" >/dev/null 2>&1; then
mkdir -p "$tmp/pds.iconset"
for s in 16 32 128 256; do
sips -z $s $s "$tmp/pds.png" --out "$tmp/pds.iconset/icon_${s}x${s}.png" >/dev/null 2>&1 || true
sips -z $((s * 2)) $((s * 2)) "$tmp/pds.png" \
--out "$tmp/pds.iconset/icon_${s}x${s}@2x.png" >/dev/null 2>&1 || true
done
iconutil -c icns "$tmp/pds.iconset" \
-o "$APP/Contents/Resources/pds.icns" >/dev/null 2>&1 || true
fi
fi
[[ -n "$tmp" && -d "$tmp" ]] && rm -rf "$tmp"
touch "$APP" # nudge Finder into re-reading the bundle
print ""
print "Done. Launch it from $APP_DIR, or: open -a \"$APP_NAME\""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment