## Summary of the Pull Request Adds `PowerToys.LightSwitch.CLI.exe` for querying and controlling the running Light Switch service. Scripts can read status, set or toggle the configured theme targets, and enable or disable the existing schedule, with JSON output and documented exit codes. Closes #49904. ## PR Checklist - [x] Closes: #49904 - [x] **Communication:** Discussed with core contributors. - [x] **Tests:** Added/updated; 140 native, 221 managed CLI, and 49 Light Switch settings tests pass locally. - [x] **Localization:** User-facing text uses localized resources with English fallback. JSON fields, protocol values, and exit codes remain invariant. - [x] **Dev docs:** Added/updated. - [x] **New binaries:** Added to the solution, signing list, CLI shim manifest, and installer shim components. - [ ] **Documentation updated:** External PowerToys documentation PR. ## Detailed Description of the Pull Request / Additional comments - `src/modules/LightSwitch/LightSwitch.Cli/` adds `status`, `light`, `dark`, `toggle`, `schedule enable [--mode]`, and `schedule disable`. All commands support `--json`; help and version work without the service. Enabling a schedule from `Off` requires an explicit existing mode: `fixed-hours`, `sunset-to-sunrise`, or `follow-night-light`. - `src/modules/LightSwitch/LightSwitchService/CliPipeServer.cpp` and `CliProtocol.cpp` implement bounded, versioned request/response IPC with user/session/logon isolation, server identity checks in the client, timeouts, and shutdown handling. The CLI requires a running service and does not automatically retry mutations after an uncertain result. - `src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.cpp` exposes serialized theme operations and status snapshots using the existing scheduling rules. `src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp` submits counted toggle requests to the service so theme writes and manual-override updates occur together. Theme access failures and partial changes are reported to callers; verified recovery preserves downstream theme notifications. - Schedule commands update the existing `scheduleMode` setting and complete the worker's settings handling before replying. Duplicate settings notifications preserve manual overrides, and settings loaded before a delayed notification are not mistaken for an external theme change. Overrides account for sleep, clock changes, and unavailable Night Light data. `src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs` refreshes after settings-file replacement. Concurrent editors retain the existing whole-file save behavior. - CLI help, text labels, and service diagnostics use localized resources. Available CLI and parser satellite resources are packaged without requiring missing translations. Machine-readable protocol and JSON values remain stable. - `tools/CliShim/CliShimManifest.props`, `installer/PowerToysSetupVNext/CliShims.wxs`, and `.pipelines/ESRPSigning_core.json` register deployment and signing. The CLI and service are included in BugReport diagnostics. Usage, protocol behavior, and exit codes are documented in `doc/devdocs/modules/lightswitch-cli.md`. ## Validation Steps Performed - Latest x64 Debug builds passed for `LightSwitchService.UnitTests`, `LightSwitch.Cli.UnitTests`, and `Settings.UI.UnitTests`, including their Light Switch service, CLI, and Settings UI dependencies. Each build returned exit code 0 with 0 warnings and 0 errors. - 140 native service tests passed via `vstest.console.exe`, covering protocol/identity/framing, shutdown and queue handling, settings persistence, manual overrides, recovery, and schedule boundaries. - 221 managed CLI tests passed via `vstest.console.exe`, covering parsing, output/exit codes, bounded IPC, response validation, and resource-based output. - 49 Light Switch settings tests passed via `vstest.console.exe` with `FullyQualifiedName~LightSwitch`, covering the ViewModel and settings watcher. This was a filtered run, not the complete Settings test suite. - `git diff --check` passed. The cleanup preserves behavior, removes redundant theme-observation and observer-stop calls, and removes unused imports; existing regression tests cover the affected paths. Desktop theme/hotkey acceptance, real translated-satellite acceptance, ARM64/Release verification, and full installer acceptance are not covered by these latest checks. --------- Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
10 KiB
Light Switch CLI
PowerToys.LightSwitch.CLI.exe queries and controls the running Light Switch
service in the current Windows user session. Enable Light Switch in PowerToys
before using it. The CLI does not start PowerToys or enable the module.
On its first startup, the service creates the default settings if the module's settings file does not exist: scheduling is off and both theme targets are enabled. Existing, malformed, or unreadable files are never replaced with defaults. This also supports enabling the module through preconfigured general settings or policy before visiting the settings page.
The PATH-visible executable is a CLI shim. Its target,
PowerToys.LightSwitch.Cli.exe, is installed in the PowerToys installation root.
Terminals opened before installation may need to be reopened to pick up PATH.
Commands
PowerToys.LightSwitch.CLI.exe status
PowerToys.LightSwitch.CLI.exe status --json
PowerToys.LightSwitch.CLI.exe light
PowerToys.LightSwitch.CLI.exe dark
PowerToys.LightSwitch.CLI.exe toggle
PowerToys.LightSwitch.CLI.exe schedule disable
PowerToys.LightSwitch.CLI.exe schedule enable --mode fixed-hours
PowerToys.LightSwitch.CLI.exe schedule enable --mode sunset-to-sunrise
PowerToys.LightSwitch.CLI.exe schedule enable --mode follow-night-light
light and dark set the targets selected in Light Switch settings (system,
applications, or both). Repeating the same command does not toggle the manual
override flag. A theme different from the current automatic plan is held using
Light Switch's existing manual override behavior, until a scheduled boundary or
a relevant settings change.
toggle uses the same service operation as the shortcut. It retains the existing
behavior: invert each selected target, toggle manual override, then evaluate the
schedule. In particular, mixed system/application themes can be reconciled to
the automatic plan when the operation releases an existing override.
schedule disable saves the existing scheduleMode property as Off.
schedule enable --mode selects one of the existing modes and uses the values
already saved for its times, coordinates, and offsets. It changes only the
schedule mode; it does not reset time values or record a previous mode.
Without --mode, schedule enable succeeds without changing an already-enabled
schedule. If the mode is Off, it fails with exit code 2 and asks for an explicit
mode. Re-selecting the current mode does not clear a manual theme override.
All commands support --json. --help and --version work without the service.
Boolean options also accept explicit values, such as --help=true,
--version:true, and --json false. A false help or version option does not
suppress the command. Options after the literal -- remain arguments.
Parser directives such as [parse] and [suggest] are unsupported and are
rejected as arguments, including when supplied through response files.
Output and exit codes
Successful service responses use a versioned JSON envelope:
{
"version": 1,
"success": true,
"state": {
"systemTheme": "dark",
"appsTheme": "dark",
"changeSystem": true,
"changeApps": true,
"scheduleMode": "FixedHours",
"manualOverride": true
}
}
The mode is the service's accepted configuration, not its last applied-mode
cache. System and application themes are read separately. Reading a theme can
fail; an error response may include a state with that value set to unknown.
Errors contain version, success: false, and an error object with stable
code and human-readable message fields. When available, state describes the
observed result, including partial theme changes. JSON output is one object on
stdout. Without --json, human-readable errors go to stderr; logs do not mix
with JSON.
Help, text-mode labels, and human-readable messages use localized resources,
with English fallback when a translation is unavailable. The service produces
its messages in its own UI language. JSON property names, types, theme and
schedule-mode values, error codes, version numbers, and exit codes stay invariant.
Only the human-readable help and error.message fields may vary by language;
consumers should inspect success, error.code, and structured state values.
JSON serialization escapes translated text, including quotes and line breaks.
| Exit code | Meaning |
|---|---|
| 0 | The command completed successfully. |
| 1 | Execution, theme access, or protocol failure. |
| 2 | Invalid arguments or invalid configuration. |
| 3 | The service is unavailable or cannot accept the request. |
| 4 | Timeout or a lost connection after sending a request; the result may be unknown. |
The shim can additionally return its documented 9009-9011 startup errors.
A successful schedule command has saved the mode and completed the service's settings handling, including any required Night Light observer lifecycle change. Observer initialization runs asynchronously. The settings window continues to refresh through its existing file watcher. Concurrent settings editors retain the existing whole-file save behavior: a later save, including a stale UI snapshot, can replace an earlier update. The CLI does not add field-level conflict merging.
A request timeout does not roll back Windows changes. The CLI never
automatically retries a mutation, especially toggle. Query status before
deciding whether to issue another command. Windows theme broadcasts can take
time; successful theme application does not mean every application has finished
redrawing or that a downstream PowerDisplay profile has completed.
Implementation
The C# CLI parses arguments with the repository's pinned System.CommandLine package. It connects to the service through a duplex named pipe, verifies the server executable, sends one request, reads one response, and closes.
The pipe name is PowerToys_LightSwitch_Cli_<sessionId>. Its access control
restricts callers to the service's user/logon scope and rejects remote clients.
After reading a bounded frame, the service obtains the connection's identification
token and verifies its user SID, desktop session, and logon SID. This accepts the
same user's linked UAC tokens while rejecting independent logons. The service
reverts to its own identity before parsing or executing commands.
Wire messages are single-line JSON encoded as BOM-less UTF-16LE, with a maximum
of 32,768 UTF-16 code units per request or response. Version 1 requests contain
version, command, and, only for schedule-enable, an optional mode.
Wire command names are status, light, dark, toggle, schedule-enable,
and schedule-disable. Wire modes use the existing setting values:
FixedHours, SunsetToSunrise, and FollowNightLight. Unknown versions,
commands, modes, and unexpected properties are rejected before execution.
Theme commands and snapshots use the existing StateManager mutex. A command uses one configuration snapshot throughout its theme application and state update. Schedule requests are handed to the existing service worker because it owns the Night Light observer's lifetime. Observer joins never run while holding the StateManager lock.
The shortcut submits a counted toggle request to the service instead of modifying Windows and sending a delayed completion notification. The Command Palette's existing toggle event still reaches that shortcut adapter. This preserves the number of requests received by the module and avoids an old completion event cancelling a newer CLI override. Both the shortcut and Command Palette request path restart an exited service before submitting their toggle. This does not change the standalone CLI's requirement for an already-running service.
Manual overrides use a full local date/time baseline. Scheduled boundaries that pass during sleep expire an override, while a backward clock adjustment does not masquerade as midnight. Sun times are cached by their full calendar date. New manual commands account for already-occurring schedule or Night Light transitions before establishing their override; delayed notifications cannot cancel that newer choice. Unchanged settings notifications preserve detected external theme choices while genuine plan changes still take effect.
The worker also reconciles Night Light on minute ticks, recovering from a settings read or theme write failure without requiring another notification. An unavailable Night Light value is not treated as off: automatic scheduling waits for a successful read, preserving the last confirmed state and any manual override. A manual command issued while Night Light is unreadable establishes its boundary baseline on recovery, so an older, unobserved transition does not cancel the newer choice. If a scheduled theme write takes effect but its readback fails, PowerDisplay is notified once the same planned state is confirmed. New plans and successful manual commands supersede that pending notification; a failed manual write does not discard it. Notifications use the verified theme snapshot without rereading it immediately before sending the event. An explicit theme command also acknowledges an external choice that has not yet been polled, even when no Windows write is needed. If a command fails, external changes on targets it did not write are still handled against the current plan; the command's own partial writes are not treated as external overrides. A later successful retry can confirm an earlier write whose verification failed without losing the PowerDisplay notification. Calculated sun times share the schedule command's settings lock and are saved only while their effective configuration remains current. An older calculation cannot restore a mode that a completed schedule command has replaced.
Validation
The managed tests cover argument parsing, text/JSON output, exit codes, bounded pipe reads, server identity, and response validation. Native tests cover strict request parsing, pipe lifetime/framing, theme operations with fake Windows dependencies, and settings handling without changing the desktop theme.
Manual acceptance should include all three target selections, mixed themes, shortcut/CLI sequences, Off-to-mode transitions, all three automatic modes, delayed settings notifications, settings read/write failures, and stopping the module while a client is connected.