Skip to content

feat(input): Bring Steam Deck trackpads to touchscreen handhelds - #676

Open
Faga250885 wants to merge 8 commits into
armada-os:mainfrom
Faga250885:feat/steam-deck-virtual-trackpads-pr
Open

Faga250885 wants to merge 8 commits into
armada-os:mainfrom
Faga250885:feat/steam-deck-virtual-trackpads-pr

Conversation

@Faga250885

@Faga250885 Faga250885 commented Oct 8, 2026 •

Copy link
Copy Markdown

Summary

Add opt-in virtual Steam Deck trackpads for touchscreen handhelds. Touch regions feed the Steam Deck controller target through InputPlumber, allowing Steam Input to recognize the left and right pads and use their touch/motion for mouse control, virtual/radial menus and other trackpad mappings.

This makes cursor-heavy games and interfaces more practical on handhelds without physical trackpads: strategy games, adventure games, inventories, launchers and Steam's virtual menus are useful examples. The master switch starts off, so normal touchscreen behavior remains the default.

The latest test image has been installed and tested on my Retroid Pocket 6 and is working very well. This includes the latest revision with full-screen single-pad modes and automatic orientation. Other hardware still needs hands-on validation, especially dual-screen routing and different vibration motors.

Demonstration

Virtual Steam Deck trackpads on a Retroid Pocket 6

Watch the demonstration. Some settings and labels have changed since the video was recorded; the behavior below describes the current implementation.

Layouts

The mode selector has a short description immediately below it. Full-screen single-pad modes are explicitly distinguished from split screen.

Mode Touch behavior Pad graphics
Bottom corners One left pad and one right pad in the lower corners. Configurable
Four corners Both left corners control the logical left pad; both right corners control the logical right pad. Configurable
Follow first touch Each pad is anchored where the first contact begins in its half of the screen. Configurable; no center marker
Split screen The left half controls the left pad; the right half controls the right pad. None
Full screen: left The entire selected touchscreen controls the left pad. The right pad is disabled. None
Full screen: right The entire selected touchscreen controls the right pad. The left pad is disabled. None

In either full-screen mode, dragging across the screen's midpoint continues controlling the same pad without a half-screen boundary or a change of pad identity. A stationary short tap still produces a click.

The screen-wide modes enforce their logical pad selection in the backend as well as the UI. Their zone, visual and per-pad controls are disabled. Returning through the mode selector to a two-pad layout enables both pads again. Master toggles and the controller shortcut preserve the selected mode.

Features and behavior

  1. Steam Input-recognized left/right trackpads. Steam Deck controller emulation must be selected first. Touch and motion are injected into its pad capabilities, allowing Steam Input trackpad mappings and virtual menus. This is more than an absolute touchscreen-to-mouse mapping.
  2. Master switch and per-side controls. The master switch enables the feature. For the configurable layouts, either logical pad can be enabled independently. Whole-screen modes select their required side automatically.
  3. Shared manual size and corner offset. Pad size is adjustable from 15–80% of screen height, with a 35% default. Corner offset is adjustable. No physical-size preset or per-model physical-dimension table is required.
  4. Automatic touchscreen orientation. The input service normalizes raw touch using Armada's existing ARMADA_PANEL_ORIENTATION convention. There are no user-facing rotation or mirror controls and no additional per-device touchscreen-orientation variable.
  5. Optional visible-area restriction. Input pauses when a held contact leaves a pad and resumes when it re-enters. A contact that starts outside can also enter a pad without lifting first. While capture is active, touches outside the pad regions are not forwarded as normal screen taps.
  6. Immediate tap-to-left-click. A short tap normally sends BTN_LEFT through a virtual mouse using uinput. If that sink is unavailable, the daemon falls back to a native pad press. This is not pressure sensing, and the normal tap path should not be described as a freely remappable native Deck press.
  7. Steam-directed trackpad haptics. Steam supplies pulse timing through InputPlumber; a leased adapter scales the strength for the handheld's motors. Strength is adjustable from 0–100, with zero suppressing virtual-trackpad pulses. The input daemon does not add duplicate movement-based synthetic vibration, and ordinary game rumble takes priority on shared hardware. The physical sensation depends on the actuators and is not claimed to be identical to the Deck's dedicated trackpad hardware.
  8. Dedicated Gamescope overlay. A transparent, non-interactive GTK/Cairo surface draws above Steam and games through a dedicated compositor marker. MangoHud is not required. The input daemon owns touch capture; the overlay window does not intercept input.
  9. White-only appearance settings. External border width of 1–10 px, border opacity up to 50%, roundness of 0–100%, and dotted, solid or no background. Dots are static and round, with configurable size and spacing. Background and center-indicator opacity also cap at 50%. The opacity limits are conservative visual choices, not a guarantee against OLED burn-in.
  10. Visibility and preview controls. Visible pads can remain displayed or hide automatically after 1–5 seconds. Editing appearance previews the pads. An optional center dot marks fixed pad locations while hidden. Split-screen and full-screen single-pad modes draw no pad graphics, regardless of saved appearance settings.
  11. Game Mode / Desktop policy and touch blocking. Game Mode-only is enabled by default and restores normal touch on Desktop. The shortcut can still change the enabled setting from Desktop, while capture obeys the session policy. A separate touchscreen-block switch is mutually exclusive with virtual trackpads.
  12. Configurable controller shortcut. Choose 1–4 different buttons and either immediate activation or a three-second hold. The default is L3 + R3 held for three seconds. Buttons still reach games, and the menu warns about binding conflicts; this is not presented as a conflict-free Steam chord.
  13. Shortcut acknowledgement. Successful toggles show a rounded, two-second enabled/disabled chip in Game Mode, including when turning pads off or using a screen-wide mode. Desktop uses a session notification. Compatible rumble hardware can provide three brief acknowledgement pulses.
  14. Primary/secondary touchscreen selection. Capture and overlay follow the selected touchscreen and its Gamescope session. The selector is always visible; on single-screen devices it is disabled with an explanation. An unavailable secondary session does not silently redirect capture to another touchscreen. This uses Armada's existing dual-screen device configuration.
  15. Dedicated settings tab, descriptions and reset. The Virtual Trackpads tab explains each mode beneath the selector. Restore Defaults resets settings and turns the feature off so normal touch returns.
  16. Shared Armada Control layout fixes. Focus/hover backgrounds stay within the menu column, slider endpoints remain visible, and scrolling leaves space above Steam's controller footer. The correction applies across Armada Control tabs, not only Trackpads.

Defaults

Master off; both pads selected; bottom corners; Game Mode-only on; shared size 35%; corner offset 8 px; visible-area restriction on; haptics 60%; auto-hide after one second; 2 px outer border at 30% opacity; 24% roundness; white 1 px dots with 4 px spacing at 30% opacity; center indicator off. The optional shortcut defaults to L3 + R3 held for three seconds.

Service lifecycle

Turning the trackpads off stops their touchscreen worker and releases capture, contacts and haptic ownership. The rendering worker also exits when unused, except for the bounded two-second shortcut acknowledgement.

  • When Game Mode-only is selected, entering Desktop stops capture and restores normal touch.
  • Split-screen and full-screen single-pad modes need no persistent rendering worker. Their shortcut notice can briefly start the overlay, which exits when the notice expires.
  • The separate touchscreen-block option intentionally requires capture while enabled.
  • The optional shortcut listener remains active while pads are off so the controller can turn them back on. It observes physical controller events without grabbing them. It does not capture the touchscreen, create a virtual mouse, inject pad events or create graphics. Disabling the shortcut stops that listener too.
  • Worker lifetimes are reconciled by the existing Armada Control service.

Implementation and review scope

Component Responsibility
armada-control Validate settings and manage capture, shortcut and overlay lifetimes.
armada_virtual_trackpads.py Defaults, configuration normalization, shared pad geometry, automatic touch orientation, session gating and shortcut timing.
virtual-trackpads Exclusive multitouch capture, logical pad routing, tap handling and haptic ownership lease.
virtual-trackpads-shortcut Optional physical-button observer for the configurable toggle.
virtual-trackpads-overlay / armada_overlay_session.py Transparent drawing, notices, validated Xwayland access and primary/secondary Gamescope routing.
InputPlumber patches 0005/0006 Asynchronous normalized multitouch injection and native haptic pulse adaptation.
Gamescope patch 0028 Dedicated non-interactive overlay layer above Steam/game content.
Armada Control frontend Settings tab, shortcut editor, live settings and scoped shared menu layout fixes.

The new runtime dependencies are python3-evdev, python3-cairo, libXfixes and libnotify; GTK/PyGObject are already part of the image.

The feature UI is English-only in this PR. Existing Armada locales remain intact; reviewed translations can be submitted separately.

Physical-size presets, physical-dimension quirks, the separate ARMADA_TOUCHSCREEN_ORIENTATION variable, and manual rotation/mirroring have been removed. Existing saved touchRotation and touchMirror values are ignored when loading configuration and omitted on the next save, so an old setting cannot silently retain a rotated layout.

Current upstream changes, including the AYANEO haptics polling fix, are preserved. The two virtual-pad InputPlumber patches follow the existing upstream patches. The unused React overlay implementation was removed; GTK is the rendering implementation.

Fork-only test/publishing workflows, registry-mirror changes and the scx-scheds build workaround are not included in this PR. The latest feature change is a separate commit, 9d62156, for focused review.

Validation

Physical testing

I installed the latest experimental image on my Retroid Pocket 6, tested the updated implementation and confirmed that it works very well before updating this PR. Previous RP6 testing also covered Steam Input recognition, cursor/click use, Steam virtual menus, overlay visibility, haptics, shortcut notices and menu layout.

This is RP6 validation, not a claim of verified behavior on every supported handheld. Dual-screen hardware and other vibration motors still need testers.

Automated tests and published test image

Build and validation run 37998156772, test-image commit 05ed631, completed successfully:

  • Geometry/configuration checks, including automatic orientation, legacy-setting migration, complete screen coverage, edge coordinates, side exclusivity and transition back to split screen.
  • 37 lifecycle, configuration, shortcut and capture tests, including continuous motion across the midpoint, short-tap clicks on either full-screen pad, correct side selection after shortcut toggles, and on-demand overlay behavior.
  • 5 Python haptic-ownership tests.
  • 11 overlay/session tests, including real GTK/Cairo rendering under Xvfb, transparent screen-wide modes, shortcut chips, automatic exit without a graphics session, legacy orientation settings and primary/secondary routing.
  • InputPlumber rebuilt successfully. Its package checks passed, including all six armada_virtual_haptics Rust tests covering strength scaling, fade, mute, ordinary-rumble priority and lease validation/expiry.
  • Kernel build, final container validation and publication succeeded. The container job was retried after an interrupted Fedora layer download; the successful retry reused the completed tests and packages.

Frontend validation also passed: 31 tests, locale parity/runtime-label checks, TypeScript checking and the plugin build.

The tested image contains the same feature/runtime code as PR commit 9d62156. The only source differences in test commit 05ed631 are .github/workflows/build.yml and packages/scx-scheds/build.sh, used exclusively to build and publish the experimental image.

Reproducing the checks

bash tests/virtual-trackpads-test.sh
(cd decky/armada-control && npm ci && npm test && npx tsc --noEmit && npm run build)
dbus-run-session -- /usr/bin/python3 tests/virtual-trackpads-overlay-test.py

The graphical suite requires Linux, GTK 4, PyGObject with Cairo support, Pycairo, Xvfb, xauth, libXfixes and a D-Bus session. The general shell suite also runs the geometry/lifecycle/haptic checks through just test; the graphical suite is a separate command.

Input constraints and relationship to #502

The configured shortcut still reaches games and can conflict with their bindings. Exclusive touchscreen capture also needs coordination with other features that grab the same device.

#502 provides per-game touchscreen-to-relative-mouse behavior. This implementation feeds left/right Steam Deck touchpad inputs into Steam Input, enabling trackpad mappings and Steam virtual menus. They address related needs through different input paths.

The aim is to make this a maintainable part of Armada. Feedback on architecture, defaults, shortcut integration and testing on additional devices is welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants