Add AYN button toggle for a bottom-screen performance HUD#90
Open
brycesub wants to merge 35 commits into
Open
Conversation
With the bottom panel enabled in the kernel, the top screen becomes DSI-2 and a second backlight (ae94000.dsi.0) appears that Steam picks up for brightness control instead of the top panel's. - ayn-thor.conf: primary connector is DSI-2; declare the primary backlight, secondary connector, and both touchscreen device names - steamos-priv-write: accept backlight brightness writes and steer them to ARMADA_PRIMARY_BACKLIGHT, rescaling between brightness ranges (bottom is 0-255, top is 0-4096) - desktop-bootstrap/setup-dual-screen: one-time desktop layout for dual-screen devices - primary on top at priority 1, secondary centered below, each touchscreen pinned to its own output (KWin otherwise maps both to the same screen) Requires the armada-packages kernel with the Thor bottom panel enabled; on the old kernel DSI-2 does not exist and gamescope falls back to the only connected output, so ordering is safe. Fixes virtudude#28 (phase 1: no bottom-screen UI in gaming mode yet) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
Gamescope only lights up the primary panel, but the secondary touchscreen still feeds it input - fingers resting on the dark glass move Steam's cursor. Inhibit the device (via /sys/class/input/*/inhibited, made wheel-writable like the other session-managed sysfs knobs) when the gaming session starts and re-enable it when the desktop session starts. A future Armada Control toggle could expose this as a user choice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
kscreen-doctor -o -j prints the human-readable listing after the JSON document, so json.loads raised JSONDecodeError and the script died before applying the layout or pinning the touchscreens. On first boot that left both touchscreens mapped to the same output: taps on the top screen landed on the bottom desktop, where a stray double-tap on the Return to Game Mode icon logged the session out. Request JSON alone and parse only the first document so trailing text can never take the script down again. Verified on an AYN Thor: layout applied, both touchscreens pinned to their own panel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeRkgMBMwWNcnwMk4j5kVx
The perf-ACL udev rule made every backlight's brightness group-writable, so Steam's direct sysfs write (its first choice before falling back to steamos-polkit-helpers/steamos-priv-write) succeeded on whichever backlight it enumerated first - on Thor the secondary bottom panel. The priv-write steering to ARMADA_PRIMARY_BACKLIGHT never engaged except for out-of-range values, so the brightness slider dimmed the desktop screen instead of the gaming one. Observed on hardware: at Steam startup a saved 0-4096-scale value hit the 255-max bottom panel (EINVAL, then rescaled to 166 and correctly steered 2666 -> top panel via priv-write), but ordinary slider writes were in-range for the bottom panel and landed there directly. The udev grant now goes through backlight-acl, which withholds it from every backlight when ARMADA_PRIMARY_BACKLIGHT is set: all Steam writes fail EACCES and funnel through the steering helper, the same path Steam uses on stock SteamOS where backlight sysfs is not user-writable. Single-backlight devices keep the direct grant. Part of virtudude#28 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011roK8EGErjD8eEjjZNNW9w
With ARMADA_GAMING_SESSION=nested (Thor), gaming mode runs as a nested gamescope fullscreen on the primary screen inside the persistent desktop session, with the plasma desktop live on the secondary screen. Mode switches become near-instant: SwitchToDesktopMode just ends the nested gamescope (the desktop is already underneath) and SwitchToGameMode launches it via a transient user unit. steamos-manager handles the switch in its session-bus instance only; the embedded session-swap path is unchanged for single-screen devices and for the system-bus instance. Validated on AYN Thor: instant switch both directions through Steam's UI, game performance indistinguishable from embedded gaming mode. Part of virtudude#28 phase 2 (design doc in docs/superpowers/specs/) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
armada-session-default resets the SDDM session to "gamemode" each boot; on ARMADA_GAMING_SESSION=nested devices that session is now the plasma session, and desktop-bootstrap launches the nested gamescope on a fresh session start. Boot lands in gaming mode with the desktop already live on the secondary screen, and the embedded gamescope session is no longer used on these devices. Known limitation: nested gaming runs at the mode KWin selected for the primary output (120Hz on Thor); Steam's per-game refresh switching is not bridged yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
Steam communicates refresh switching by setting GAMESCOPE_DYNAMIC_REFRESH on gamescope's Xwayland root; the embedded DRM backend applies it but nested gamescope cannot modeset the host output. nested-refresh-bridge watches the atom on the nested Xwayland (stdbuf -oL: xprop block-buffers into pipes) and applies the nearest supported panel rate via kscreen-doctor, restoring the max rate when dynamic refresh is disabled or the gaming session ends. nested-gaming exports STEAM_DISPLAY_REFRESH_LIMITS so Steam offers the refresh UI, and Steam's in-between requests (e.g. 90 on a 60/120 panel) snap to the nearest mode, ties to the higher. Validated on hardware via the exact atom writes Steam performs: 60 -> panel 60Hz, 90 -> 120Hz, 0 -> 120Hz. Closes the known limitation from the previous commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
Pairs with the armada-packages gamescope patch that reads GAMESCOPE_NESTED_REFRESH_RATES: without it Steam's refresh slider collapses to the single current rate because the nested backend cannot enumerate the host output's modes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
Pass --prefer-output with the device's primary connector; the armada gamescope patch makes the nested Wayland backend fullscreen onto that output instead of whichever one the host compositor considered active. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
PowerDevil's idle detection does not count gamepad input as activity; with the persistent desktop session hosting nested gaming it suspends the device mid-game (fake-suspend blanks both screens and everything looks wedged until the power button or a reboot). This also explains an intermittent all-black-screens report during prototype testing. Ship a system default that disables automatic suspend on AC and battery; suspend stays available through the power button (powerbuttond -> fake-suspend), and users can re-enable idle suspend in System Settings. Low-battery behavior keeps the upstream default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XMbeBSJJG1pNfdqkUcG1gn
Two clean-install bugs, one root cause each: Steam applies client updates by exiting and relying on its wrapper to relaunch it (sometimes twice in a row, as during first-run onboarding). The embedded session gets this from SDDM Relogin restarting the whole gamescope session; nested mode had no equivalent, so the update left a Steam-less gamescope on screen and a dead gaming mode. Gaming mode now runs as armada-nested-gaming.service (user unit) with Restart=always: nested-gaming launches gamescope with the -R readiness pipe (the same handshake gamescope-session-plus uses), runs Steam as its own child with the environment gamescope would hand it (DISPLAY, GAMESCOPE_WAYLAND_DISPLAY, host WAYLAND_DISPLAY unset), and exits when Steam does so systemd brings the pair back up. StartLimit caps crash loops and PartOf= graphical-session.target stops the unit at logout instead of letting it thrash without a compositor. The desktop "Return to Game Mode" icon went steamos-session-select → os-session-select → sudo session-control switch-gamemode: a full plasma logout + SDDM relogin (~20s of desktop) even on nested devices, so impatient taps killed the Steam that was already starting - mid update-install during onboarding. os-session-select now starts/stops the unit directly when the desktop compositor is alive, like steamos-manager does; both switch directions are instant and idempotent, which also makes the icon's double-activation per tap harmless. steamos-manager and desktop-bootstrap converge on the same unit, replacing the transient systemd-run instances and the pgrep guard they needed. Validated on AYN Thor: Steam exit auto-relaunches in ~2s into a fresh gamescope; icon and D-Bus switches instant both ways including double-fired taps; explicit stop is never restarted. Part of virtudude#28 phase 2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011roK8EGErjD8eEjjZNNW9w
A gamescope-wl the unit does not own (stale instance from an older deployment, manual launch) survived SwitchToDesktopMode's unit stop and left Steam waiting on "Switching to desktop..." indefinitely. Both switch paths now pkill gamescope-wl after stopping the unit; for unit-owned processes this duplicates the SIGTERM systemd already sent, so it is harmless in the normal case. Part of virtudude#28 phase 2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011roK8EGErjD8eEjjZNNW9w
KWin asks the kscreen OSD service to show its layout picker whenever two outputs appear in a combination it has not stored yet (kwin workspace.cpp, updateOutputConfiguration). On dual-screen devices that is the very first boot, so the picker pops up over Steam onboarding. The kded kscreen module is not involved on Wayland; KWin has no config knob for this. Mask the user service instead: KWin's D-Bus call is fire-and-forget, so the only trace is a one-line activation failure from dbus-broker. Display Settings still work; only the picker OSD (and the output identify overlay) go away. Validated on the AYN Thor: with kwinoutputconfig.json absent, the journal shows the activation request refused and no OSD appears. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ASwEJacP64yJTjo66FRdu9
Gaming mode covers the top screen, so the desktop panel and icons belong on the bottom one, DS-style. Make the secondary output the priority-1 display and scale it to 150% to match the primary. Rotate it per the new ARMADA_SECONDARY_ORIENTATION (the Thor's bottom panel is portrait-native like the top), and center it below the primary in post-scale logical coordinates. Validated on the AYN Thor: DSI-1 lands at priority 1, scale 1.5, rotation right, position 226,720 (centered under the 1280x720 top screen). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ASwEJacP64yJTjo66FRdu9
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Drop the standalone off state; Steam's slider already covers hiding the top overlay, so the button just switches which screen owns the HUD. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Also correct the spec: hud-top sources the session env file itself instead of EnvironmentFile=, which would silently fall back to the desktop DISPLAY the user manager holds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
Conflicts= carries no ordering, so a toggle could briefly run both MangoApps against gamescope's single-consumer stats queue. An After= edge between the HUD units makes systemd finish the old HUD's stop before the new one starts, in both directions. Also tighten the listener-guard test literal and fold the review's focus-steal and slider-tracking checks into the validation checklist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YYLGYK7FVvW3jP7WVrNjg
c5cebae to
8de7ea2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Depends on #80, which provides the nested-gaming session this HUD integrates with.
Validation
tests/*.shjust checksystemd-analyze --user verifyfor both HUD units1240x1080at the DSI-1 offset, remained fullscreen/above/skip-taskbar, and did not become the active window