Skip to content

feat: custom app mode, any .exe as the console launcher - #161

Merged
lippdev merged 2 commits into
mainfrom
feat/custom-app-mode
Oct 19, 2026
Merged

lippdev merged 2 commits into
mainfrom
feat/custom-app-mode

Conversation

@lippdev

@lippdev lippdev commented Oct 19, 2026

Copy link
Copy Markdown
Owner

What

A fourth launch mode, Custom app ("Aplicativo personalizado"): any .exe becomes the console launcher (RetroArch, another frontend, a game launcher…).

  • Settings → On entering: picking "Custom app" shows App to open (with Browse…, a FileOpenPicker filtered to .exe, initialized with the main window handle like the Playnite picker), Arguments (optional) and Process to watch (optional). The Console interface gets an App to open row (opens the picker) when this mode is selected.
  • Config: CustomAppPath, CustomAppArguments, CustomAppWatchProcess, saved like the other settings.
  • Start: same display/audio flow as the other modes, then launches the exe with its folder as working directory.
  • Window: once the watched process has a main window it is moved to the game screen (Monitors.MoveProcessWindows, up to 8 tries like the others) and brought to the front (WindowSwitcher.Activate, no topmost).
  • Watch: the session ends when the watched process (CustomAppWatchProcess if set, otherwise the launched exe's name) has appeared and then stayed gone for an 8 s grace period. If it never appears within 90 s, that's logged in Portuguese and the session stays until the user ends it (Start + Select hold, session menu, tray).
  • Validation: empty path, not an .exe, or a file that no longer exists blocks starting with a clear message, both in the view model (warning InfoBar) and in ConsoleEngine.Start (before anything changes on screen, which covers the tray/--start/protocol paths).
  • Names: summaries and the session screen show the exe's FileDescription, falling back to the file name.

Pure logic (validation, process name normalization, watched process choice, display name fallback, exit/timeout decisions) is in src/ConsoleMode/Services/CustomApp.cs, linked in the test project and covered by CustomAppTests.

Strings in all six catalogs (translated). Guide row in all six guides plus a short paragraph in GUIDE.md / GUIDE.pt-BR.md; manual checks in docs/TESTING.md; changelog in both files.

Why

Requested in #111 (Reddit): use any app as the trigger, so Console Mode applies the TV setup, watches that process, and restores the desk when it exits.

Part of #111: the keyboard hotkey to end console mode mentioned there is not included in this PR.

How tested

  • dotnet test tests/ConsoleMode.Tests/ConsoleMode.Tests.csproj on Linux: all tests pass, including the new CustomAppTests and the localization checks.
  • The WinUI app only builds on Windows: the build is left to CI.
  • Displays, process watching and window placement can't be tested in CI; the manual checks (RetroArch, launcher stub with a watched process, process that never appears, invalid path, closing the app restores the desk) are in docs/TESTING.md.

🤖 Generated with Claude Code

Adds a fourth launch mode, "customApp": launches a chosen exe (optional
arguments, working directory = its folder), moves its window to the game
screen and ends the session once the watched process (the exe, or a
process name for launcher stubs) has appeared and stayed gone for a grace
period. A process that never appears keeps the session until it's ended
by hand. A missing or invalid path blocks starting.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@lippdev
lippdev force-pushed the feat/custom-app-mode branch from cd231c4 to 20e860b Compare October 19, 2026 19:06

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The watcher can miss a short-lived launched process and then leave the console session active indefinitely; process-name matching also conflates distinct instances.

Reviewed changes This review covers the custom-app launch mode and its process/window lifecycle, configuration, UI, localization, tests, and documentation.

  • Custom app launch and validation Adds executable selection, arguments, pre-start validation, and launch from the executable's directory.
  • Session monitoring and window placement Watches a selected process name, moves its window to the game display, and restores after an observed process exits.
  • User-facing surfaces and tests Adds localized settings and console controls, guide/manual test updates, changelog entries, and pure-logic tests.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using gpt-6-luna | 𝕏

if (running)
{
State.CustomAppGoneSince = null;
if (!State.HasAppeared) AppLog.Write($"Loop: processo {name} apareceu");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the launched app exits before a poll observes it, HasAppeared stays false and the 90-second timeout only logs; the session then remains active indefinitely even though the app did run and exit. This is especially plausible for a short-lived launcher when no separate watch process is configured.

Technical details
# Do not lose exits between polls

## Affected sites
- `src/ConsoleMode/Services/ConsoleEngine.cs:495-515` — `HasAppeared` is set only by a positive `IsProcessRunning` poll, and the never-seen timeout does not exit.
- `src/ConsoleMode/Services/ConsoleEngine.cs:297` — before appearance, polling is delayed by 1.5 seconds.

## Required outcome
- Detect completion of the launched process even if it starts and exits between polling ticks, and allow the normal grace-period restoration path to run.

{
var name = State.CustomAppWatchName;
var now = DateTime.Now;
var running = LaunchService.IsProcessRunning(name);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetProcessesByName treats every same-name process as the watched app, including one that was already running before this session or belongs to another install. That process can mark the app as appeared, keep the session alive after the launched instance exits, and cause PlaceCustomAppWindow to activate the wrong instance.

Technical details
# Associate the watcher with the intended process

## Affected sites
- `src/ConsoleMode/Services/ConsoleEngine.cs:495-501` — any matching process satisfies the watch and triggers window placement.
- `src/ConsoleMode/Services/ConsoleEngine.cs:503-515` — another same-name process prevents the exit grace period from completing.
- `src/ConsoleMode/Services/ConsoleEngine.cs:522-540` — the first enumerated same-name window is moved/activated.
- `src/ConsoleMode/Services/LaunchService.cs:51-70` — process checks and window enumeration are name-wide.

## Required outcome
- Ensure the session watch and window placement refer to the intended app instance rather than an unrelated process that shares its executable name.

# Conflicts:
#	CHANGELOG.en-US.md
#	CHANGELOG.md
@lippdev
lippdev merged commit 439b6a2 into main Oct 19, 2026
5 checks passed
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.

1 participant