diff --git a/README-STEAM.md b/README-STEAM.md index bc3bd7c..f06f964 100644 --- a/README-STEAM.md +++ b/README-STEAM.md @@ -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 ``` diff --git a/documentation/BIG_PICTURE_MODE.md b/documentation/BIG_PICTURE_MODE.md index f84e4bd..6791cc0 100644 --- a/documentation/BIG_PICTURE_MODE.md +++ b/documentation/BIG_PICTURE_MODE.md @@ -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 diff --git a/main.js b/main.js index 0e7c338..6041dea 100644 --- a/main.js +++ b/main.js @@ -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(); diff --git a/preload.js b/preload.js index ca40fdd..b072151 100644 --- a/preload.js +++ b/preload.js @@ -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 - initGamepadHandler(); + // Re-initialize gamepad handler if early init failed + if (!gamepadState.initialized) { + initGamepadHandler(); + } }); // Optimized API exposure with error handling and caching diff --git a/start-steamdeck.sh b/start-steamdeck.sh new file mode 100644 index 0000000..3a9aa26 --- /dev/null +++ b/start-steamdeck.sh @@ -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