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:
+55
-9
@@ -135,22 +135,68 @@ gamepadAPI.setDebug(true);
|
||||
|
||||
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`
|
||||
2. **Check gamepad connection**: Run `gamepadAPI.getConnected()` to see detected gamepads
|
||||
3. **Press a button first**: On Linux, the `gamepadconnected` event may not fire until the first button press
|
||||
4. **Enable debug mode**: Run `gamepadAPI.setDebug(true)` to see detailed logs
|
||||
5. **Restart the app**: Close Nebula completely and relaunch from Steam
|
||||
1. **Configure Steam Input per-game** (most reliable fix):
|
||||
- In Steam, right-click Nebula → Properties → Controller
|
||||
- Set **"Override for Nebula"** to **"Disable Steam Input"**
|
||||
- This completely disables Steam's input emulation for this app
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# Via environment
|
||||
NEBULA_BIG_PICTURE=1 ./Nebula
|
||||
|
||||
# Disable Big Picture Mode
|
||||
./Nebula --no-big-picture
|
||||
|
||||
# Environment variables also work
|
||||
NEBULA_BIG_PICTURE=1 ./Nebula
|
||||
NEBULA_NO_BIG_PICTURE=1 ./Nebula
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
## ⚠️ 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
|
||||
|
||||
### 🎮 Controller Support
|
||||
|
||||
@@ -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 { autoUpdater } = require('electron-updater');
|
||||
const { pathToFileURL } = require('url');
|
||||
@@ -61,6 +96,30 @@ try {
|
||||
// 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
|
||||
gpuConfig.configure();
|
||||
|
||||
|
||||
+26
-1
@@ -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
|
||||
// =============================================================================
|
||||
@@ -234,8 +257,10 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
domReady = true;
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
// Optimized API exposure with error handling and caching
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user