Spacemouse adaptation for NX running in WINE on Linux
  • C 91%
  • Shell 8.3%
  • Makefile 0.7%
Find a file
2026-08-21 22:39:03 +10:00
scripts Prevent NX Discovery Centre 2026-08-21 22:39:03 +10:00
src Working version 2026-08-21 22:19:37 +10:00
vendor Working version 2026-08-21 22:19:37 +10:00
.gitignore Working version 2026-08-21 22:19:37 +10:00
Makefile Working version 2026-08-21 22:19:37 +10:00
README.md Working version 2026-08-21 22:19:37 +10:00

SpaceMouse support for Siemens NX under Wine

This directory is a self-contained installer for making a 3Dconnexion SpaceMouse drive Siemens NX's 3D viewport (rotate/pan/zoom) when NX is run under Wine on Linux.

It replaces the vendor's low-level siappdll.dll device-API DLL (the one the official 3DxNX.dll NX plugin loads) with a custom shim that:

  • reads the SpaceMouse directly via Wine's RawInput/HID support, and
  • drives NX's viewport directly through the NX Open C API,

bypassing the internal 3DxWare event-processing pipeline entirely, because (after extensive reverse-engineering) that pipeline was found to be non-functional for this NX/3DxWare version combination under Wine.

Result / current status

Real hardware SpaceMouse input drives 6DOF (rotate, pan, zoom) in NX, correctly axis-mapped, with no visible helper windows and no manual reconnect step beyond one initial "open a part and wiggle the knob" per NX session. This is considered production-usable. Two minor known cosmetic issues remain (see "Known limitations" below) but do not block normal use.

Architecture

SpaceMouse hardware (USB HID, VID 0x256F)
    |
    v
Wine RawInput (WM_INPUT), read directly by our shim's own background
thread -- NOT via 3DxNX.dll's internal pipeline
    |
    v
custom siappdll.dll (this project)
    - satisfies 3DxNX.dll's expected siappdll device-API surface just
      enough for it to initialize and hand control to us
    - separately: its own RawInput listener thread parses SpaceMouse HID
      reports (translation + rotation) directly
    - marshals resulting 6DOF deltas onto NX's main UI thread (required --
      calling NX Open C "set" APIs from a background thread crashes NX)
    - applies them directly via NX Open C calls:
        UF_VIEW_ask_rotation / UF_VIEW_set_rotation  (rotate)
        UF_VIEW_ask_center   / UF_VIEW_set_center    (pan)
        UF_VIEW_set_scale                            (zoom)
        UF_VIEW_ask_visible_objects + UF_MODL_ask_bounding_box
                                     + UF_VIEW_set_center
                                                     (one-time auto-recenter)
    |
    v
Siemens NX 2506 (via NXOpen C / libufun.dll, already loaded in-process)

The official 3DxNX.dll plugin is still present and still loaded by NX (we deliberately keep using it rather than reimplementing NX plugin discovery), but its own internal motion-delivery pipeline is not what actually moves the viewport -- our shim's RawInput + NX Open C path does that instead.

Why a real device-API DLL replacement, and not just an NX plugin?

3DxNX.dll expects to load a vendor-provided siappdll.dll implementing the classic 3DxWare "SpaceWare Input Library" C ABI (SiInitialize, SiOpen, SiGetEvent, ...) plus a newer GetFactory/COM-ish object surface. Our shim implements just enough of that surface for 3DxNX.dll to complete its own initialization handshake (which is a real prerequisite -- NX/3DxNX won't get far enough to even construct a device object without it), then takes over motion delivery itself instead of relying on 3DxNX's own event processing (which was proven dead for this configuration).

Why the magellan-window-helper.exe companion process?

3DxNX.dll performs a FindWindow check for a window of class MAGELLAN_3D_CONTROLLER as part of its own driver-availability detection. magellan-window-helper.exe is a small helper that registers a single hidden (SW_HIDE) window of that class and otherwise does nothing. It must be running (in the same Wine prefix) before NX starts. It has no visible UI.

The "wiggle the knob" step

3DxWare's own device-connect handshake needs one real HID report to fully recognize the device each NX session. In practice: open any part, then gently move the SpaceMouse once. After that it stays connected for the rest of the session. This is the only manual step; NX does not need to be restarted, and there is no dialog to click through.

Known limitations

  • Rotation pivot is auto-recentered once, automatically, to the model's bounding-box center on first SpaceMouse motion each session (no more manual "Fit" needed -- this used to be a manual step, now fixed).
  • Zoom occasionally choppy specifically when combined with simultaneous rotate/pan in the same motion (zoom alone, and rotate+pan alone, are both smooth). Root cause not fully identified; two throttling strategies were tried and did not help. A promising-looking undocumented API pair (UF_VIEW_ask_frustum/UF_VIEW_set_frustum) was investigated as a possible fix or alternative zoom mechanism: ask_frustum is safe and gives believable read data, but set_frustum corrupts the view on even a single call (its exact semantics don't match the naive eye-point/direction/extent model reverse-engineered from disassembly) -- do not use UF_VIEW_set_frustum without a real documented signature. Zoom is implemented via the ordinary UF_VIEW_set_scale instead.
  • Zoom can "jump" if you mix 2D-mouse zoom and SpaceMouse zoom in the same session. Cause: there is no UF_VIEW_ask_scale export, so the shim tracks its own relative scale value internally, which can desync from NX's true current scale if you zoom by other means. Workaround: stick to one zoom input method per session, or hit "Fit" to resync.

Prerequisites

Before running this installer, in the target Wine prefix you must already have:

  1. Siemens NX installed (e.g. under <prefix>/drive_c/Program Files/Siemens/NX<version>/NXBIN/ugraf.exe).
  2. The official 3Dconnexion 3DxWare driver package with the NX plugin installed (provides 3DxNX.dll under <prefix>/drive_c/Program Files/3Dconnexion/3DxWare/3DxNX/ugii_dir/v<version>/startup/, and the genuine vendor siappdll.dll in NX's NXBIN directory). This project intentionally does not install or redistribute that driver -- only replaces the one low-level DLL it loads.
  3. On the Linux side: the SpaceMouse must be usable as a raw USB HID device visible to Wine (no special Linux driver/daemon is required by this shim itself -- it reads the device directly via Wine's RawInput/HID support, which talks to the kernel HID device node).
  4. A build toolchain: x86_64-w64-mingw32-gcc (MinGW cross compiler).

Install

cd installer
make CC=x86_64-w64-mingw32-gcc
WINEPREFIX=/path/to/your/wineprefix make install

(make install just runs scripts/install.sh; you can also invoke that script directly with the prefix as an argument instead of the environment variable.)

This will:

  1. Locate NX's NXBIN directory and confirm 3DxNX.dll is present.
  2. Back up the genuine vendor siappdll.dll to siappdll_vendor.dll in the same directory (only on first install -- safe to re-run).
  3. Copy the freshly-built shim into NXBIN/siappdll.dll.
  4. Install magellan-window-helper.exe under <prefix>/drive_c/spacemouse-nx/.
  5. Generate build/start-nx.sh, a ready-to-use launcher for this prefix.

Running NX

./build/start-nx.sh

This kills any stray NX/helper processes in the prefix, starts the hidden helper, then starts NX. Wait roughly 90 seconds for NX to fully start, open any part, and gently wiggle the SpaceMouse once to connect it.

Uninstall

WINEPREFIX=/path/to/your/wineprefix make uninstall

Restores the genuine vendor siappdll.dll and removes the helper directory. NX itself is untouched.

Directory layout

installer/
  Makefile                  top-level build (siappdll.dll + helper .exe)
  README.md                 this file
  src/
    siappdll.c              the shim source (RawInput + NX Open C driver)
    siappdll.def             its DLL export table (SpaceWare ABI + factory)
    magellan_window_helper.c  the hidden helper window process
  scripts/
    install.sh               installs into a target Wine prefix
    uninstall.sh              reverts an install
    start-nx.sh.in            template used to generate build/start-nx.sh
  build/                     (generated) compiled .dll/.exe + start-nx.sh

Rebuilding after source changes

make CC=x86_64-w64-mingw32-gcc clean all
WINEPREFIX=/path/to/your/wineprefix make install
./build/start-nx.sh

Always verify the deployed DLL's checksum matches the freshly built one (install.sh prints an md5sum of both for this purpose) before testing, to avoid testing a stale build.