Lock-screen service: spike + headless send host + app-yield coordination

Local checkpoint - NOT for public release. First increment of the send-only
Windows service feature (design in memory/project_remsound_service).

THE SPIKE PASSED: the send engine runs fully headless (no window, no message
pump) and streams, proven by a real self-test — the one genuine unknown that
gated the whole feature. Also proves the app-yield model end to end.

What's in this increment (all headless, all tested, 19/19 gate):
- AppConfig: ServiceProfileName + ServiceLoggingEnabled (machine-wide).
- InteractivePresence (Core): the cross-session app-yield token. App holds a
  Global\ mutex for its lifetime; the service checks it and yields while an
  interactive app is present, resuming when it closes OR crashes (OS frees the
  mutex). Name-parameterised internal seams for isolated testing.
- ServiceSendHost (App): loads a send-only profile and streams it to its peers,
  WASAPI-only, no ASIO/receive. ApplyProfile/Suspend/Resume + a RunLoop that
  drives them from the presence token with a settle delay. v1 sends to direct
  peer addresses (LAN/port-forwarded); NAT/relay discovery stays the app's job.
- Program.cs: the interactive app now acquires the presence token at startup so
  a future service yields to it.
- Tests: "Service app-yield token" (held=present, released=absent) and "Service
  send host (headless stream + yield)" — streams a captured device to a local
  receiver over loopback, verifies start/suspend/resume, then drives the full
  RunLoop against the token (held=suspended, released=resumes-and-flows).

Still to come (later increments): the --run-service entry + Windows-service
registration, the Service menu, the 3-tab config dialog, updater integration,
docs. None user-facing yet, so nothing deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-12 14:59:31 +01:00
co-authored by Claude Opus 4.8
parent 8f61eb800d
commit e648bee531
6 changed files with 495 additions and 3 deletions
+13
View File
@@ -244,6 +244,19 @@ public sealed class AppConfig
/// Startup behaviour dialog. Null = always show the picker (legacy behaviour).</summary>
public string? StartWithProfileTitle { get; set; }
// === Lock-screen send-only service (the RemSound Windows service) ===
/// <summary>The title of the profile the RemSound Windows service loads and streams from. Null =
/// no service profile configured yet. Machine-wide, set from the Service menu's config dialog. The
/// service is send-only / WASAPI-only; this profile is edited exclusively through that dialog and is
/// kept out of the normal profile picker.</summary>
public string? ServiceProfileName { get; set; }
/// <summary>Whether the RemSound Windows service writes its own log file. Separate from the app's
/// machine-wide <see cref="LoggingEnabled"/> so you can diagnose the headless service without
/// turning on logging for the interactive app. Off by default. Set from the service config dialog's
/// "Additional options". Machine-wide.</summary>
public bool ServiceLoggingEnabled { get; set; }
/// <summary>How often RemSound polls the GitHub Releases API for a newer build. Default
/// <see cref="UpdateCheckFrequency.Every24Hours"/>. Set to <see cref="UpdateCheckFrequency.Never"/>
/// to disable background checks entirely (the user can still trigger a manual check via