Skip to content

Prooxie/GameFlow

Repository files navigation

⊕ GameFlow

Cross-platform gamepad tooling for anyone.

Ultimate multi-platform, multi-language controller tool for remapping and compability layering, Any input device in, any virtual controller out: Xbox, PlayStation or Nintendo. third-party gamepad, keyboard or/and mouse.

v1.0.1 Beta · Built with .NET 10 · Avalonia UI · SDL3 · HIDMaestro · ViGemBus

License: MIT Platform Made by Proxy Darkness


Buy Me a Coffee No pressure — using and sharing the project already helps a lot. Thank you!


What is GameFlow?

GameFlow sits between your physical input — a gamepad, keyboard, mouse, or a phone on your Wi-Fi — and any game or application. It reads that input, transforms it through a saved profile (a stack of mapping rules), and writes the result to one or more virtual controllers the game sees.

This project started as a script to spare players from repetitive inputs that were damaging both their controllers and their hands. It was later rebuilt in Python as Autofire, then in C#/.NET as the architecture you're looking at now, since renamed GameFlow to separate the product from autofire the individual feature.

For the full feature reference, per-rule mechanics, and platform-by-platform verification notes, see WIKI.md.


Features at a Glance

Category Details
Autofire Button and stick autofire, hysteresis-shaped so it doesn't jitter at the trigger threshold
Remapping Button-to-button remap with optional source suppression; toggle rules to flip other rules on/off at runtime
Shift layers "Caps Lock for your controller" — 6 activation modes (Hold, Toggle, Latch, Cycle, Sticky, No Button), long-press-to-engage, idle auto-cancel
SOCD cleaning Opposite-direction key resolution — Last-wins (Snap Tap), First-wins, or Neutral, per button pair
Stick Trim Hold a digital button, a stick modulates it into an analog trigger press — deadzone, ramp rate, and reset-on-release all configurable
Multi-source rows Many inputs → one output. Six combine modes (Maximum, Minimum, Sum, Average, Multiply, FirstActive) or a formula — a small dependency-free expression compiler (s1 - s2, if(s1>0.5, s2, 0), clamp(...)), with 10 starter recipes
Gyro aiming Local / Player / World reference frames, dual-threshold smoothing (kills tremor, keeps flicks sharp), 4 engage modes including a raw-stick gate, drift bias calibration, output to a stick or the mouse
Touchpad mapping Finger-anchored virtual stick, 8-way wedge D-pad, frame-to-frame mouse mode — works on any pad with a touch surface
Custom scripting Sandboxed Lua per control (MoonSharp), for logic the built-in rule types don't cover
Button combos One press → a timed sequence of virtual presses
Freeze macro Captures the stick vector on the rising edge of a button; optional pulse-while-frozen
Per-device tuning Deadzone, anti-deadzone, full-at, sensitivity, response curve, and invert — per stick, per trigger, per slot AND per device, so the same pad feels different on two different slots. Rumble/lighting/adaptive-trigger settings save and reload too (see Known Limitations for what's live vs. saved-only)
Keyboard & mouse as gamepad Full keyboard state (not just a handful of buttons) synthesized into a gamepad snapshot — works on Windows, Linux, and macOS
Phone as controller No app install — open a URL in any phone browser on the LAN. Dual anchored sticks, 8-way D-pad, analog triggers, rumble via the Vibration API, and the phone's own gyroscope/accelerometer feed the same gyro pipeline a DualSense uses. Up to 16 phones at once
Multiple virtual controllers Each "slot" is an independent input → profile → output pipeline, with its own device assignment, output kind, and output provider
Profile system Create, duplicate, rename, import, export — JSON, human-editable
Live dashboard Physical and virtual controllers rendered side by side, per slot

Requirements

All platforms

Requirement Version
.NET SDK 10.0 or later
Git Any recent version

SDL3 native library (required everywhere)

Gamepad/joystick input goes through SDL3 on every platform:

Platform File Location
Windows SDL3.dll Next to GameFlow.App.exe
Linux libSDL3.so System library path, or the app directory
macOS libSDL3.dylib System library path (e.g. /usr/local/lib), or the app directory

An optional gamecontrollerdb.txt (SDL_GameControllerDB) in the application directory extends the built-in mapping database.

Windows — virtual controller output

GameFlow uses HIDMaestro exclusively: user-mode, no kernel driver, no reboot. Drop HIDMaestro.Core.dll next to GameFlow.App.exe and it activates on next launch. If it isn't available, that slot has no output — GameFlow won't silently substitute something else, and the log and the slot's displayed name both say why. The app runs fine either way; the slot falls back to Preview (dashboard-only, no real device).

Windows also has the fullest platform support: keyboard/mouse-as-gamepad, mouse output (SendInput), and the only real virtual-gamepad output backend.

Linux

  • Gamepad input: SDL3, works out of the box.
  • Keyboard/mouse-as-gamepad: real, via direct evdev reads (/dev/input/eventN) — not a Windows-only feature anymore.
  • Mouse output: real, via uinput (creates an actual virtual input device).
  • Virtual gamepad output: not built yet — Preview only (see Known Limitations).

Needs the input group for device access without root:

sudo usermod -aG input $USER
# then log out and back in

macOS

  • Gamepad input: SDL3, works out of the box.
  • Keyboard/mouse-as-gamepad: real, via CGEventTap — reads system-wide (no per-device distinction; see the wiki).
  • Mouse output: real, via CGEventPost.
  • Virtual gamepad output: not built yet — Preview only.

Keyboard/mouse capture needs Input Monitoring permission (System Settings → Privacy & Security → Input Monitoring) — without it the app runs fine, that one source just reads as empty.

---

Quick Start

git clone https://github.com/Prooxie/GameFlow.git
cd GameFlow
dotnet build GameFlow.sln
dotnet run --project src/GameFlow.App

First launch creates a default profile under:

Platform Location
Windows %LOCALAPPDATA%/AutofireNext/
Linux \~/.local/share/AutofireNext/
macOS \~/Library/Application Support/AutofireNext/

Still named AutofireNext on purpose — it predates the rename, and changing it would orphan existing installs. Nobody types or sees this path; the app itself always says GameFlow.

---

How to Use

Basics

  1. Profiles tab → create a profile, or start from the default.
  2. Devices tab → confirm your physical device is listed.
  3. Virtual controllers panel → Add controller to create a slot, assign a device, pick an output kind and provider, enable it.
  4. Dashboard → confirm the virtual side (marked with a VIRTUAL badge) mirrors your input through the mapping.

A slot's own virtual output is hidden from every input picker — you can't feed a virtual controller back in as a source.

Tune a device (deadzones, curves, rumble, lighting, adaptive triggers)

Click any virtual controller panel on the Dashboard. The panel outlines on hover, and clicking opens that slot's tuning editor — five tabs: Sticks, Triggers, Rumble, Lighting, Adaptive. Stick and trigger changes are live immediately. Rumble/lighting/adaptive settings save per slot per device but don't reach physical hardware yet (the panel says so).

Phone as a controller

  1. Dashboard → Web Controller → enable it.
  2. Open the shown URL (e.g. http://10.0.0.5:8080) on a phone on the same Wi-Fi.
  3. Pick a layout, tap Enable motion for gyro aiming.
  4. The phone appears in the device list as an ordinary gamepad — assign it to a slot like any other device.

**Windows firewall note:** binding to all interfaces needs an admin URL ACL, or the server falls back to localhost-only and phones can't reach it (the log says which mode it's in):

netsh http add urlacl url=http://+:8080/ user=Everyone

Add mapping rules

Profiles tab → + Add rule against any control. Rules apply in declared order, every polling tick, live — no separate "apply" step.


Configuration

src/GameFlow.App/appsettings.json:

{
  "Runtime": {
    "DashboardRefreshHz": 165,
    "StartRuntimeOnLaunch": true,
    "DefaultCulture": "en",
    "Updates": {
      "RepoOwner": "Prooxie",
      "RepoName": "GameFlow",
      "AssetNamePattern": "GameFlow-{tag}-{rid}.zip",
      "UserAgent": "GameFlow-UpdateChecker"
    }
  }
}
Key Default Description
DashboardRefreshHz 165 UI refresh rate for the live dashboard
StartRuntimeOnLaunch true Whether the input/output runtime starts automatically
DefaultCulture en Fallback UI language
Updates:RepoOwner / RepoName Prooxie / GameFlow GitHub-releases update checker

Profiles are JSON in the application data directory (see Quick Start), editable by hand or entirely through the Profiles tab.


Providers

Input: sdl (all platforms — gamepads/joysticks everywhere, plus keyboard/mouse synthesis via platform-native raw input), web (phone browsers over the LAN), demo (animated preview, no hardware), none.

Output: hidmaestro (Windows, real virtual gamepad), preview (all platforms, dashboard-only). Mouse-cursor output (separate from gamepad output) is real on all three platforms via SendInput / uinput / CGEventPost.

Older profiles referencing retired providers (XInput, GameInput, x360ce, ViGEm, PS3, and similar) migrate to sdl/hidmaestro automatically.

---

Project Structure

GameFlow/
├── src/
│   ├── GameFlow.App              # Avalonia UI, ViewModels, Views, Localization
│   ├── GameFlow.Core              # Domain models, mapping pipeline, rule types, formula engine
│   └── GameFlow.Infrastructure    # SDL3, HIDMaestro, platform input/output, web controller, profiles
├── tests/
│   ├── GameFlow.Core.Tests        # Pipeline, rules, formula compiler, gyro maths
│   └── GameFlow.Infrastructure.Tests  # Platform interop, protocol, device catalog
└── .github/workflows/             # CI: build, test, and tag-triggered release packaging

Architecture Overview

Physical input                 Keyboard/mouse            Phone browser
  (SDL3 gamepad/joystick)     (Win Raw Input /              (WebSocket)
        │                      Linux evdev /                    │
        │                      macOS CGEventTap)                │
        ▼                            ▼                          ▼
     InputSource.ReadAsync() / ReadDevice() ─────────────────────┘
                    │
                    ▼
     DeviceSettingsProcessor      (per-device deadzone/curve, before merge)
                    │
                    ▼
     ControllerMappingPipeline    (one per active slot, isolated)
        ├── RuleToggleRule             ├── StickTrimRule
        ├── SocdCleanRule              ├── GyroMapRule
        ├── ButtonRemapRule            ├── TouchpadMapRule
        ├── ButtonAutofireRule         ├── MultiSourceMapRule (+ Formula)
        ├── MultiButtonAutofireRule    ├── ControlScriptRule (Lua)
        ├── ButtonComboRule            └── StickThresholdRule
        ├── StickAutofireRule
        └── FreezeLastDirectionRule
                    │
                    ▼
     OutputSink.WriteAsync()      (HIDMaestro · Preview)
     + IMouseOutputWriter          (SendInput · uinput · CGEventPost, independent of gamepad output)
                    │
                    ▼
     Virtual controller seen by games — hidden from GameFlow's own
     input list, so it can never be selected back in as a source.

Shift layers resolve first each tick, gating which rules are active. Every slot's rules live in its profile's JSON, applied in order, live. The dashboard reads from lock-free snapshot stores on its own timer, independent of the mapping pipeline's tick rate.


Known Limitations

Documented here rather than discovered by surprise:

  • Rumble, RGB/lighting, and adaptive triggers save and reload correctly, but don't reach physical hardware yet. They need a dedicated effects thread — direct SDL writes were found to block the runtime tick over Bluetooth, so this is deliberately not wired in until that thread exists.
  • No virtual gamepad output on Linux or macOS. Mouse output is real on both; a real virtual controller (via uinput's gamepad mode, or DriverKit on macOS) is future work.
  • Bundled controller theme placement is known-imperfect on some skins — several were generated from asset-pack sprites without authoritative layout data. Fixing this properly needs template-matching each sprite against its base image; tracked, not yet done.
  • CGEventTap (macOS) has no per-device keyboard/mouse distinction — one aggregate stream for the whole system, not per-physical-device like Windows/Linux.
  • Remote Link, DSU/Cemuhook motion server, per-app profile switching — on the roadmap, not started.

Localization

🇬🇧 English · 🇨🇿 Czech · 🇩🇪 German · 🇪🇸 Spanish · 🇫🇷 French · 🇮🇹 Italian · 🇵🇱 Polish · 🇷🇺 Russian — switchable live from Settings.


Contributing

  1. Fork the repository
  2. git checkout -b feature/my-feature
  3. Commit your changes
  4. Open a Pull Request

Keep commits focused with a short description of what changed and why. See WIKI.md for how the mapping pipeline and rule system fit together before adding a new rule type.


License

MIT — see LICENSE.


Acknowledgments

Check out my other projects on GitHub, YouTube, and Twitch.


Thanks to

Nazzareno96 — keeping me sane during development, beta testing twitch.tv/nazzareno96

NoobKillerRoof — voicing real hardware/software pain points that inspired this project, beta testing twitch.tv/noobkillerroof

About

Ultimate multi-platform, multi-language controller tool for remapping and compability layering, Any input device in, any virtual controller out: Xbox, PlayStation or Nintendo. third-party gamepad, keyboard or/and mouse. Built on SDL3, HIDMaestro or VigemBus, HidHide, and .NET 10 and Avalonia.

Topics

Resources

Stars

Watchers

Forks

Releases

Sponsor this project

Packages

Contributors

Languages