Improve Steam Deck controller support and documentation

Adds environment variable setup and early gamepad initialization to main.js and preload.js to ensure native controller support on Steam Deck and SteamOS. Updates README-STEAM.md and BIG_PICTURE_MODE.md with detailed instructions for disabling Steam Input emulation and troubleshooting. Introduces start-steamdeck.sh launcher script to automate environment configuration for Steam Deck users.
This commit is contained in:
2025-12-30 21:47:40 +13:00
parent 705efa04e0
commit ddf5bf49be
5 changed files with 237 additions and 11 deletions
+55 -9
View File
@@ -135,22 +135,68 @@ gamepadAPI.setDebug(true);
If Steam is still applying mouse emulation: If Steam is still applying mouse emulation:
1. **Verify gamepad polling is active**: Open DevTools (F12) and run `gamepadAPI.getState()` - check that `isPolling` is `true` 1. **Configure Steam Input per-game** (most reliable fix):
2. **Check gamepad connection**: Run `gamepadAPI.getConnected()` to see detected gamepads - In Steam, right-click Nebula → Properties → Controller
3. **Press a button first**: On Linux, the `gamepadconnected` event may not fire until the first button press - Set **"Override for Nebula"** to **"Disable Steam Input"**
4. **Enable debug mode**: Run `gamepadAPI.setDebug(true)` to see detailed logs - This completely disables Steam's input emulation for this app
5. **Restart the app**: Close Nebula completely and relaunch from Steam
2. **Verify gamepad polling is active**: Open DevTools (F12) and run `gamepadAPI.getState()` - check that `isPolling` is `true`
3. **Check gamepad connection**: Run `gamepadAPI.getConnected()` to see detected gamepads
4. **Press a button first**: On Linux, the `gamepadconnected` event may not fire until the first button press
5. **Enable debug mode**: Run `gamepadAPI.setDebug(true)` to see detailed logs
6. **Restart the app**: Close Nebula completely and relaunch from Steam
### Steam Launch Options ### Steam Launch Options
Add these to your Steam launch options (Right-click game → Properties → Launch Options):
```bash
# Disable Steam Input completely (recommended for native controller support)
SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=0 %command%
# Force native gamepad without Steam's emulation layer
STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD=0 %command%
# Combined - full native controller mode with Big Picture UI
SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=0 STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD=0 %command% --big-picture
# If you need to debug controller issues
SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=0 %command% --big-picture 2>&1 | tee ~/nebula-debug.log
``` ```
# Force Big Picture Mode
### Steam Deck Recommended Setup
For the best experience on Steam Deck:
1. **Add Nebula as a Non-Steam Game** (if not using Steamworks version)
2. **Controller Settings**:
- Right-click Nebula → Properties → Controller
- Set to **"Disable Steam Input"**
3. **Launch Options**:
```
SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=0 STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD=0 %command% --big-picture
```
4. **Shortcuts** (optional):
- Configure gamepad shortcuts in Steam for Steam button actions (screenshots, etc.)
### Why This Is Needed
Steam Deck / SteamOS Game Mode applies "Desktop Configuration" mouse/keyboard emulation to apps that don't appear to handle controller input. Even though Nebula polls `navigator.getGamepads()` continuously, Steam's input layer initializes before the app can signal its intent.
The solution is two-fold:
1. **Environment variables** (`SDL_GAMECONTROLLER_*`) signal to Steam's SDL-based input layer early
2. **Steam Input settings** ("Disable Steam Input") bypasses the emulation entirely
### Force Big Picture Mode
```bash
# Via command line
./Nebula --big-picture ./Nebula --big-picture
# Via environment
NEBULA_BIG_PICTURE=1 ./Nebula
# Disable Big Picture Mode # Disable Big Picture Mode
./Nebula --no-big-picture ./Nebula --no-big-picture
# Environment variables also work
NEBULA_BIG_PICTURE=1 ./Nebula
NEBULA_NO_BIG_PICTURE=1 ./Nebula NEBULA_NO_BIG_PICTURE=1 ./Nebula
``` ```
+30
View File
@@ -2,6 +2,36 @@
Nebula Browser includes a **Big Picture Mode** - a controller-friendly, console-style interface designed for Steam Deck, handheld devices, and living room setups. Nebula Browser includes a **Big Picture Mode** - a controller-friendly, console-style interface designed for Steam Deck, handheld devices, and living room setups.
## ⚠️ Steam Deck: Disabling Mouse Emulation
If Steam is emulating mouse/keyboard input with the joysticks (overriding native controller support), you need to configure Steam Input:
### Quick Fix - Steam Launch Options
Add this to your Steam launch options (Right-click → Properties → Launch Options):
```bash
SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=0 STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD=0 %command% --big-picture
```
### Recommended Fix - Disable Steam Input Per-Game
1. In Steam, right-click **Nebula****Properties****Controller**
2. Set **"Override for Nebula"** to **"Disable Steam Input"**
3. This completely disables Steam's input emulation for this app
### Using the Steam Deck Launcher Script
For the easiest setup, use the included launcher script:
1. Set Steam launch options to: `./start-steamdeck.sh`
The script automatically sets all necessary environment variables.
See [README-STEAM.md](../README-STEAM.md) for detailed troubleshooting.
---
## Features ## Features
### 🎮 Controller Support ### 🎮 Controller Support
+59
View File
@@ -1,3 +1,38 @@
// =============================================================================
// STEAM DECK / STEAMOS CONTROLLER INPUT FIX
// =============================================================================
// These environment variables MUST be set before Electron/Chromium initializes.
// They signal to Steam's input layer that this application handles its own
// controller input and should NOT have mouse/keyboard emulation applied.
//
// Without these, Steam assumes the app needs Desktop mouse emulation when running
// in Game Mode, which overrides the app's native gamepad support.
// =============================================================================
// Tell SDL (and by extension Steam Input) that this app uses the gamepad API
// SDL_GAMECONTROLLERCONFIG is used by SDL to know about controllers
process.env.SDL_GAMECONTROLLERCONFIG = process.env.SDL_GAMECONTROLLERCONFIG || '';
// Signal that this app handles gamepad input natively
// This prevents Steam from applying mouse emulation in Game Mode
process.env.SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD = '1';
// Prevent Steam from remapping the controller to keyboard/mouse
// Setting to '1' tells Steam we want raw controller access
process.env.SDL_GAMECONTROLLER_IGNORE_DEVICES = '';
// Disable Steam's overlay input hooks for this process if possible
process.env.SteamNoOverlayUIDrawing = process.env.SteamNoOverlayUIDrawing || '0';
// Tell Steam Input we're a native controller app
// When STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD is 0, Steam won't virtualize the gamepad
if (!process.env.STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD) {
process.env.STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD = '0';
}
// Hint that this is a game/controller-focused app
process.env.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS = '1';
const { app, BrowserWindow, ipcMain, session, screen, shell, dialog, Menu, clipboard, webContents } = require('electron'); const { app, BrowserWindow, ipcMain, session, screen, shell, dialog, Menu, clipboard, webContents } = require('electron');
const { autoUpdater } = require('electron-updater'); const { autoUpdater } = require('electron-updater');
const { pathToFileURL } = require('url'); const { pathToFileURL } = require('url');
@@ -61,6 +96,30 @@ try {
// Non-fatal: some environments may not allow commandLine changes at this time. // Non-fatal: some environments may not allow commandLine changes at this time.
} }
// =============================================================================
// GAMEPAD / CONTROLLER CHROMIUM FLAGS
// =============================================================================
// Enable native gamepad support in Chromium - helps with Steam Deck compatibility
try {
// Enable raw gamepad access (bypasses Steam's virtualization when possible)
app.commandLine.appendSwitch('enable-gamepad-extensions');
// Ensure the Gamepad API is enabled and working
app.commandLine.appendSwitch('enable-blink-features', 'GamepadExtensions');
// On Linux/Steam Deck, this can help with gamepad detection
if (process.platform === 'linux') {
// Disable Chromium's sandbox for gamepad access if having issues
// (Only needed in some SteamOS configurations)
// app.commandLine.appendSwitch('no-sandbox');
// Use the system's gamepad config rather than Chromium's built-in
app.commandLine.appendSwitch('enable-features', 'WebGamepad');
}
} catch (e) {
console.warn('[Gamepad] Failed to set Chromium gamepad flags:', e.message);
}
// Configure GPU settings before app is ready // Configure GPU settings before app is ready
gpuConfig.configure(); gpuConfig.configure();
+26 -1
View File
@@ -224,6 +224,29 @@ window.addEventListener('beforeunload', () => {
} }
}); });
// =============================================================================
// EARLY GAMEPAD INITIALIZATION - Critical for Steam Deck
// =============================================================================
// Initialize gamepad polling as EARLY as possible to signal Steam Input
// that this app handles controller input natively. This MUST happen before
// Steam decides to apply mouse/keyboard emulation.
//
// We try to initialize immediately when preload runs, not waiting for DOMContentLoaded,
// because Steam's input layer makes decisions very early in the process lifecycle.
// =============================================================================
// Try immediate initialization (works in most Electron contexts)
try {
if (typeof navigator !== 'undefined' && navigator.getGamepads) {
// Start polling immediately - this is the key signal to Steam
initGamepadHandler();
console.log('[NebulaGamepad] Early initialization successful - Steam should recognize controller input');
}
} catch (e) {
// Will retry on DOMContentLoaded
console.log('[NebulaGamepad] Early init deferred, will retry on DOM ready');
}
// ============================================================================= // =============================================================================
// DOM READY & INITIALIZATION // DOM READY & INITIALIZATION
// ============================================================================= // =============================================================================
@@ -234,8 +257,10 @@ window.addEventListener('DOMContentLoaded', () => {
domReady = true; domReady = true;
console.log("Browser UI loaded."); console.log("Browser UI loaded.");
// Initialize gamepad handler for Steam Deck/SteamOS support // Re-initialize gamepad handler if early init failed
if (!gamepadState.initialized) {
initGamepadHandler(); initGamepadHandler();
}
}); });
// Optimized API exposure with error handling and caching // Optimized API exposure with error handling and caching
+66
View File
@@ -0,0 +1,66 @@
#!/bin/bash
# =============================================================================
# NEBULA BROWSER - Steam Deck / SteamOS Launch Script
# =============================================================================
# This script is designed for launching Nebula on Steam Deck in Game Mode.
# It sets necessary environment variables to disable Steam's mouse/keyboard
# emulation so the app's native controller support works properly.
#
# Usage:
# 1. Add Nebula as a Non-Steam game (or via Steamworks)
# 2. Set launch options to: ./start-steamdeck.sh
# OR
# 3. Use this script's environment variables in Steam Launch Options:
# SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=0 %command% --big-picture
# =============================================================================
cd "$(dirname "$0")"
# =============================================================================
# STEAM INPUT CONFIGURATION
# =============================================================================
# These variables tell Steam's input layer that this app handles controller
# input natively and should NOT have mouse/keyboard emulation applied.
# Disable Steam's virtual gamepad layer
export SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD=0
export STEAM_INPUT_ENABLE_VIRTUAL_GAMEPAD=0
# Allow raw gamepad access
export SDL_GAMECONTROLLER_IGNORE_DEVICES=""
# Allow background gamepad events (useful when app doesn't have focus)
export SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS=1
# Hint to SDL that we're using gamepads natively
export SDL_GAMECONTROLLERCONFIG="${SDL_GAMECONTROLLERCONFIG:-}"
# =============================================================================
# NEBULA CONFIGURATION
# =============================================================================
# Enable Big Picture Mode for controller-friendly UI
export NEBULA_BIG_PICTURE=1
# Enable GPU acceleration on Linux
export NEBULA_GPU_ALLOW_LINUX=1
# =============================================================================
# LAUNCH
# =============================================================================
# Check if we're in an AppImage/AppDir or dev environment
if [ -f "./nebula" ]; then
# Packaged AppDir
exec ./nebula --big-picture "$@"
elif [ -f "./Nebula" ]; then
# Alternate launcher name
exec ./Nebula --big-picture "$@"
elif command -v npm &> /dev/null && [ -f "package.json" ]; then
# Development environment
npm start -- --big-picture "$@"
else
echo "Error: Could not find Nebula executable or npm"
echo "Make sure you're running this script from the Nebula directory"
exit 1
fi