Skip to content

Latest commit

 

History

History
713 lines (521 loc) · 16.5 KB

File metadata and controls

713 lines (521 loc) · 16.5 KB

Platform-Specific Notes

This document covers platform-specific considerations, requirements, and best practices for using nih_plug_vstgui on Windows, macOS, and Linux.

Table of Contents

Windows

Requirements

  • Operating System: Windows 10 or later (Windows 7/8 may work but are not officially supported)
  • Compiler: Visual Studio 2019 or later with C++ development tools
  • SDK: Windows 10 SDK
  • Architecture: x64 and ARM64 supported

Build Configuration

Visual Studio Setup

Install Visual Studio with the following components:

  • Desktop development with C++
  • Windows 10 SDK (latest version)
  • CMake tools for Windows

Environment Variables

The build system should automatically detect Visual Studio, but you can set these if needed:

# Set Visual Studio path
$env:VSINSTALLDIR = "C:\Program Files\Microsoft Visual Studio\2022\Community"

# Set Windows SDK version
$env:WindowsSDKVersion = "10.0.22621.0"

Platform-Specific Code

Window Handle

Windows uses HWND for window handles:

use nih_plug::prelude::ParentWindowHandle;

match parent {
    ParentWindowHandle::Win32Hwnd(hwnd) => {
        // hwnd is a *mut c_void pointing to the HWND
        frame.open(hwnd)?;
    }
    _ => panic!("Expected Win32 window handle"),
}

DPI Awareness

Windows has complex DPI scaling behavior. VSTGUI handles most of this automatically, but you should:

  1. Declare DPI awareness in your plugin manifest
  2. Use logical coordinates (VSTGUI will scale to physical pixels)
  3. Test on displays with different DPI settings (100%, 125%, 150%, 200%)

Common Issues

Issue: Plugin doesn't load in some hosts

  • Cause: Missing Visual C++ Redistributable
  • Solution: Include redistributable with your installer or link statically

Issue: Window appears but is blank

  • Cause: Graphics driver compatibility
  • Solution: Update graphics drivers, test on different hardware

Issue: High CPU usage

  • Cause: Excessive redrawing or inefficient GDI operations
  • Solution: Use invalid_rect() instead of invalid(), minimize redraws

Testing

Test on:

  • Windows 10 (various versions)
  • Windows 11
  • Different DPI settings (100%, 125%, 150%, 200%)
  • Multiple monitors with different DPI settings
  • Different graphics hardware (Intel, NVIDIA, AMD)

Debugging

Use Visual Studio debugger:

# Build with debug symbols
cargo build

# Launch DAW with debugger
devenv /debugexe "C:\Program Files\DAW\DAW.exe"

Set breakpoints in Rust code and step through execution.

Distribution

When distributing your plugin:

  1. Code Signing: Sign your DLL with a valid certificate

    signtool sign /f certificate.pfx /p password /t http://timestamp.digicert.com plugin.dll
  2. Dependencies: Include or statically link:

    • Visual C++ Runtime
    • Any additional DLLs
  3. Installer: Use WiX, Inno Setup, or NSIS to create an installer

  4. Testing: Test on clean Windows installations

macOS

Requirements

  • Operating System: macOS 10.13 (High Sierra) or later
  • Compiler: Xcode Command Line Tools (Clang)
  • Architecture: x86_64 and ARM64 (Apple Silicon) supported
  • Frameworks: Cocoa, QuartzCore, Accelerate

Build Configuration

Xcode Setup

Install Xcode Command Line Tools:

xcode-select --install

Verify installation:

clang --version

Universal Binaries

To build for both Intel and Apple Silicon:

# Build for x86_64
cargo build --target x86_64-apple-darwin --release

# Build for ARM64
cargo build --target aarch64-apple-darwin --release

# Create universal binary
lipo -create \
    target/x86_64-apple-darwin/release/libplugin.dylib \
    target/aarch64-apple-darwin/release/libplugin.dylib \
    -output plugin.dylib

Platform-Specific Code

Window Handle

macOS uses NSView for window handles:

use nih_plug::prelude::ParentWindowHandle;

match parent {
    ParentWindowHandle::AppKitNsView(ns_view) => {
        // ns_view is a *mut c_void pointing to the NSView
        frame.open(ns_view)?;
    }
    _ => panic!("Expected AppKit NSView handle"),
}

Retina Display Support

VSTGUI automatically handles Retina displays, but you should:

  1. Use logical coordinates (VSTGUI scales to physical pixels)
  2. Provide @2x bitmap resources for Retina displays
  3. Test on both Retina and non-Retina displays

Dark Mode

macOS supports system-wide dark mode. VSTGUI can adapt to this:

// VSTGUI will automatically use dark mode colors if the system is in dark mode
// You can also manually check and respond to dark mode changes

Common Issues

Issue: Plugin crashes on Apple Silicon

  • Cause: Not built for ARM64 or Rosetta 2 issues
  • Solution: Build universal binary or ARM64-specific binary

Issue: Code signing errors

  • Cause: Plugin not signed or signed incorrectly
  • Solution: Sign with valid Developer ID certificate

Issue: Gatekeeper blocks plugin

  • Cause: Plugin not notarized
  • Solution: Notarize your plugin with Apple

Issue: Graphics glitches

  • Cause: Metal/OpenGL compatibility issues
  • Solution: Update to latest macOS, test on different hardware

Testing

Test on:

  • macOS 10.13 through latest version
  • Intel and Apple Silicon Macs
  • Retina and non-Retina displays
  • Light and dark mode
  • Different DAWs (Logic Pro, Ableton Live, etc.)

Debugging

Use LLDB debugger:

# Build with debug symbols
cargo build

# Launch DAW with debugger
lldb -- /Applications/DAW.app/Contents/MacOS/DAW

Or use Xcode's Instruments for profiling:

instruments -t Leaks /Applications/DAW.app/Contents/MacOS/DAW

Distribution

When distributing your plugin:

  1. Code Signing: Sign with Developer ID certificate

    codesign --force --sign "Developer ID Application: Your Name" \
        --options runtime \
        --entitlements entitlements.plist \
        plugin.vst3
  2. Notarization: Submit to Apple for notarization

    # Create zip
    ditto -c -k --keepParent plugin.vst3 plugin.zip
    
    # Submit for notarization
    xcrun notarytool submit plugin.zip \
        --apple-id your@email.com \
        --team-id TEAMID \
        --password app-specific-password
    
    # Staple notarization ticket
    xcrun stapler staple plugin.vst3
  3. Installer: Use pkgbuild or create DMG

  4. Testing: Test on clean macOS installations

Entitlements

Create entitlements.plist for hardened runtime:

<?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>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
</dict>
</plist>

Linux

Requirements

  • Operating System: Ubuntu 20.04+, Fedora 34+, or equivalent
  • Compiler: GCC 7+ or Clang 5+
  • Display Server: X11 (Wayland via XWayland)
  • Architecture: x86_64 and ARM64 supported

Build Configuration

Dependencies

Install required development libraries:

Ubuntu/Debian:

sudo apt-get update
sudo apt-get install \
    build-essential \
    cmake \
    libx11-dev \
    libxcursor-dev \
    libxrandr-dev \
    libxinerama-dev \
    libxi-dev \
    libcairo2-dev \
    libfontconfig1-dev \
    libfreetype6-dev \
    libpango1.0-dev

Fedora:

sudo dnf install \
    gcc-c++ \
    cmake \
    libX11-devel \
    libXcursor-devel \
    libXrandr-devel \
    libXinerama-devel \
    libXi-devel \
    cairo-devel \
    fontconfig-devel \
    freetype-devel \
    pango-devel

Arch:

sudo pacman -S \
    base-devel \
    cmake \
    libx11 \
    libxcursor \
    libxrandr \
    libxinerama \
    libxi \
    cairo \
    fontconfig \
    freetype2 \
    pango

Platform-Specific Code

Window Handle

Linux uses X11 Window ID:

use nih_plug::prelude::ParentWindowHandle;

match parent {
    ParentWindowHandle::X11Window(window_id) => {
        // window_id is a u32, convert to pointer for VSTGUI
        let window_ptr = window_id as *mut c_void;
        frame.open(window_ptr)?;
    }
    _ => panic!("Expected X11 window handle"),
}

Display Connection

VSTGUI needs access to the X11 display:

// VSTGUI will automatically connect to the display specified by $DISPLAY
// Ensure DISPLAY environment variable is set

Common Issues

Issue: Cannot open display

  • Cause: DISPLAY environment variable not set
  • Solution: export DISPLAY=:0

Issue: Window doesn't appear on Wayland

  • Cause: XWayland compatibility issue
  • Solution: Force X11 mode: GDK_BACKEND=x11 your-daw

Issue: Font rendering looks bad

  • Cause: Font configuration or Cairo settings
  • Solution: Install better fonts, configure fontconfig

Issue: High CPU usage

  • Cause: Inefficient X11 operations or excessive redraws
  • Solution: Use invalid_rect(), minimize X11 round-trips

Issue: Crashes with "BadWindow" error

  • Cause: Invalid window ID or window destroyed
  • Solution: Verify window ID is valid, check window lifecycle

Testing

Test on:

  • Ubuntu 20.04, 22.04, 24.04
  • Fedora 38, 39, 40
  • Arch Linux (rolling release)
  • Different desktop environments (GNOME, KDE, XFCE)
  • X11 and Wayland (via XWayland)
  • Different window managers
  • HiDPI displays

Debugging

Use GDB debugger:

# Build with debug symbols
cargo build

# Launch DAW with debugger
gdb --args /usr/bin/daw

Or use Valgrind for memory issues:

valgrind --leak-check=full --show-leak-kinds=all /usr/bin/daw

Distribution

When distributing your plugin:

  1. Binary Compatibility: Build on oldest supported distro (e.g., Ubuntu 20.04)

  2. Dependencies: Either:

    • Statically link everything (large binary)
    • Document required system libraries
    • Provide AppImage or Flatpak
  3. File Locations: Follow Linux standards

    • VST3: ~/.vst3/ or /usr/lib/vst3/
    • Resources: /usr/share/your-plugin/
  4. Testing: Test on multiple distributions

Headless Testing

For CI/CD, use Xvfb for headless testing:

# Start virtual X server
Xvfb :99 -screen 0 1024x768x24 &
export DISPLAY=:99

# Run tests
cargo test

# Stop Xvfb
killall Xvfb

Cross-Platform Considerations

Coordinate Systems

All platforms use the same coordinate system in VSTGUI:

  • Origin at top-left
  • X increases to the right
  • Y increases downward
  • Coordinates are in logical pixels (scaled for DPI)

DPI Scaling

Each platform handles DPI differently:

Windows:

  • System DPI setting (100%, 125%, 150%, 200%)
  • Per-monitor DPI awareness
  • VSTGUI queries system DPI and scales automatically

macOS:

  • Retina displays have 2x pixel density
  • VSTGUI uses logical coordinates, scales to physical pixels
  • @2x resources for Retina displays

Linux:

  • X11 DPI settings (often 96 DPI default)
  • Some desktop environments have scaling factors
  • VSTGUI respects X11 DPI settings

File Paths

Use platform-independent path handling:

use std::path::PathBuf;

// Get platform-specific paths
let resources_dir = if cfg!(target_os = "windows") {
    PathBuf::from(r"C:\Program Files\YourPlugin\Resources")
} else if cfg!(target_os = "macos") {
    PathBuf::from("/Library/Application Support/YourPlugin/Resources")
} else {
    PathBuf::from("/usr/share/your-plugin/resources")
};

Keyboard Shortcuts

Different platforms use different modifier keys:

Windows/Linux:

  • Ctrl for shortcuts (Ctrl+C, Ctrl+V)
  • Alt for menu access

macOS:

  • Cmd (⌘) for shortcuts (Cmd+C, Cmd+V)
  • Ctrl for right-click

Handle this in your code:

let modifier = if cfg!(target_os = "macos") {
    Modifiers::CMD
} else {
    Modifiers::CTRL
};

Mouse Behavior

Windows:

  • Right-click for context menus
  • Middle-click for special actions
  • Scroll wheel for vertical scrolling
  • Shift+scroll for horizontal scrolling

macOS:

  • Right-click or Ctrl+click for context menus
  • Two-finger scroll for vertical/horizontal
  • Pinch to zoom (trackpad)

Linux:

  • Right-click for context menus
  • Middle-click for paste (X11 selection)
  • Scroll wheel behavior varies by desktop environment

Font Rendering

Each platform has different font rendering:

Windows:

  • ClearType (subpixel rendering)
  • DirectWrite API
  • System fonts: Segoe UI, Consolas

macOS:

  • Quartz (subpixel rendering)
  • Core Text API
  • System fonts: San Francisco, Monaco

Linux:

  • FreeType + Cairo
  • Fontconfig for font selection
  • System fonts vary by distribution

Use platform-appropriate fonts:

let font_name = if cfg!(target_os = "windows") {
    "Segoe UI"
} else if cfg!(target_os = "macos") {
    "San Francisco"
} else {
    "Sans" // Generic sans-serif
};

Color Management

Windows:

  • sRGB color space by default
  • ICM color management available

macOS:

  • Display P3 color space on newer displays
  • ColorSync color management
  • Automatic color space conversion

Linux:

  • sRGB color space typically
  • Color management varies by desktop environment

For consistent colors across platforms, use sRGB color space.

Performance Characteristics

Windows:

  • GDI/GDI+ can be slow for complex graphics
  • Direct2D is faster but requires Windows 7+
  • Hardware acceleration available

macOS:

  • Quartz is generally fast
  • Metal acceleration on newer systems
  • Retina displays require more pixels

Linux:

  • Cairo performance varies
  • X11 network transparency can add latency
  • Hardware acceleration depends on drivers

Optimize for the slowest platform, test on all platforms.

Build Times

Typical build times (release mode, clean build):

Windows:

  • ~5-10 minutes (Visual Studio)
  • Slower due to Windows Defender scanning

macOS:

  • ~3-7 minutes (Clang)
  • Faster on Apple Silicon

Linux:

  • ~3-7 minutes (GCC/Clang)
  • Fastest on modern hardware

Use sccache or cargo-cache to speed up rebuilds.

Testing Strategy

  1. Primary Platform: Develop on your primary platform
  2. Regular Testing: Test on other platforms weekly
  3. CI/CD: Automate builds and tests for all platforms
  4. Beta Testing: Get users to test on various configurations
  5. Release Testing: Thorough testing on all platforms before release

Continuous Integration

Example GitHub Actions workflow:

name: CI

on: [push, pull_request]

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    
    steps:
    - uses: actions/checkout@v2
      with:
        submodules: recursive
    
    - name: Install dependencies (Linux)
      if: runner.os == 'Linux'
      run: |
        sudo apt-get update
        sudo apt-get install libx11-dev libcairo2-dev
    
    - name: Build
      run: cargo build --release
    
    - name: Test
      run: cargo test --release

Best Practices

  1. Test Early, Test Often: Test on all platforms throughout development
  2. Use Platform Abstractions: Let VSTGUI handle platform differences when possible
  3. Document Platform Requirements: Clearly document what's needed on each platform
  4. Provide Platform-Specific Installers: Make installation easy for users
  5. Monitor Platform-Specific Issues: Track issues separately for each platform
  6. Keep Dependencies Updated: Update VSTGUI and other dependencies regularly
  7. Use CI/CD: Automate building and testing on all platforms
  8. Get User Feedback: Beta test on real user systems

Resources

Windows

macOS

Linux

Cross-Platform