- Drop simulated-controller fallback: simulation now only runs when explicitly enabled in Settings (or outside Tauri), so the app no longer claims a controller is connected when none is plugged in - Derive connection type from the kernel bus type (USB/Bluetooth) instead of an unreliable udev property - Read real battery percentage from /sys/class/power_supply; hide the field entirely when the device reports none - Remove Input mode and Firmware version fields (not obtainable) - Query virtual controller status from the backend instead of a hardcoded per-device flag - Exclude the app's own uinput virtual controller from device discovery - Rescan for controllers indefinitely while disconnected so a controller plugged in at any time is detected without restarting - Strip leftover debug instrumentation from the previous session Co-authored-by: Cursor <cursoragent@cursor.com>
8BitDo Control Center
A desktop configuration utility for 8BitDo Ultimate controllers on Linux. It reads the physical controller through evdev, runs input through a configurable transform pipeline (calibration, deadzones, response curves, remapping), and outputs a remapped virtual controller through uinput — without depending on any proprietary 8BitDo software or undocumented USB/firmware protocols.
Built with Tauri 2, React + TypeScript, Tailwind CSS, and Rust.
Status: Device discovery and read-only evdev input observation are implemented. The virtual-controller output pipeline remains opt-in work in progress; until it is enabled, the app never changes physical controller output.
Features (current)
- Dark, light, and high-contrast themes
- Profile management: create, rename, duplicate, delete, reset, import, export —
persisted as versioned JSON under
~/.config/8bitdo-control-center/profiles/ - Five built-in profiles (Default, FPS, Racing, Platformer, Custom)
- Reusable, generic SVG controller diagram (front + rear views, clickable hotspots)
- Button remapping (button/key/mouse/disabled, with turbo/toggle/hold/long-press/ double-tap modes)
- Analog stick tuning: inner/outer/anti-deadzone, radial/axial mode, sensitivity, inversion, circularity correction, smoothing, response curve presets, with a live raw-vs-processed preview and a draggable pad to simulate input
- Trigger tuning: deadzone, max activation, sensitivity, hair-trigger, digital mode, inversion, response curve, with a live preview
- Motion page (gyro + vibration controls, hidden when unsupported)
- Input tester: live button/stick/trigger/gyro readout, poll rate estimate, and a scrollable raw event log, drivable via a simulated controller
- Device information page
- Linux evdev discovery/capability probing and live raw input event streaming for detected physical gamepads, with a simulated fallback for development
- Explicit Apply / Revert / Save Profile controls with unsaved-changes warnings
Getting started (Linux Mint / Ubuntu-based)
Prerequisites
# Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Tauri Linux system dependencies
sudo apt update
sudo apt install -y libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev \
libsoup-3.0-dev libgtk-3-dev librsvg2-dev libudev-dev \
libayatana-appindicator3-dev libssl-dev build-essential curl wget file pkg-config
Node.js 20+ and npm are also required.
Run in development
npm install
npm run tauri dev
Build a release bundle
npm run tauri build
Project structure
src/ React + TypeScript frontend
├── components/
│ ├── layout/ Sidebar, TopBar, AppLayout, PageShell
│ ├── controller/ Generic SVG controller diagram + hotspots
│ ├── inputs/ Slider, Toggle, Select, TextField, SimulatedStickPad
│ ├── feedback/ Toasts, confirm dialog, unsaved-changes bar
│ ├── visualizations/ Stick/trigger preview, event log
│ └── common/ Panel, Button, Badge, StatusDot
├── pages/ One component per sidebar destination
├── hooks/ useTheme, useController, useProfileInit, ...
├── services/ Tauri command wrappers + the mock controller backend
├── stores/ Zustand stores (controller, profile, settings, ui)
├── types/ Mirrors of the Rust profile/controller structs
├── utils/ Deadzone/curve math shared by every preview
└── constants/ Canonical control IDs, nav items
src-tauri/ Rust backend (Tauri 2)
├── src/
│ ├── commands/ Tauri command handlers (profiles, system)
│ ├── profile_schema.rs Versioned profile data model + migration
│ ├── profile_manager.rs Disk-backed profile CRUD
│ └── default_profiles.rs Built-in profile templates
Roadmap
- Phase 1 — Shell & scaffolding (current) — app shell, theming, profile management, controller visualization, and a simulated controller backend so every page is usable before real hardware is wired up.
- Phase 2 — Real device visibility —
device_detection/evdev_reader, capability probing, live Device Information + Input Tester pages. - Phase 3 — Core remap pipeline —
uinput_writer, the realinput_transformdeadzone/sensitivity pipeline, and Apply/Revert/Save wired to the live pipeline. - Phase 4 — Profiles in depth — shortcut-combo/process-based profile switching, the calibration wizard, and the custom curve point editor.
- Phase 5 — Advanced mapping & polish — macros, shift layers, motion/vibration against real hardware, diagnostics/support bundle, udev rules + setup script, packaging, and the full Rust test suite.
Permissions
The app never needs to run as root. A later phase adds a udev rule and setup script
that grant a dedicated group access to the controller's evdev nodes and /dev/uinput.
See Troubleshooting and the detailed development roadmap for permissions and hardware-mode guidance.