--- author: Dustin Howett @DHowett-MSFT created on: 2019-07-19 last updated: 2019-11-05 issue id: "#2563" --- # Improvements to CloseOnExit ## Abstract This specification describes an improvement to the `closeOnExit` profile feature and the `ITerminalConnection` interface that will offer greater flexibility and allow us to provide saner defaults in the face of unreliable software. ### Conventions and Terminology The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119). ## Inspiration Other terminal emulators like ConEmu have a similar feature. ## Solution Design ### `ITerminalConnection` Changes * The `TerminalConnection` interface will be augmented with an enumerator and a set of events regarding connection state transitions. * enum `TerminalConnection::ConnectionState` * This enum attempts to encompass all potential connection states, even those which do not make sense for a local terminal. * The wide variety of values will be useful to indicate state changes in a user interface. * `NotConnected`: All new connections will start out in this state * `Connecting`: The connection has been initiated, but has not yet completed connecting. * `Connected`: The connection is active. * `Closing`: The connection is being closed (usually by request). * `Closed`: The connection has been closed, either by request or from the remote end terminating successfully. * `Failed`: The connection was unexpectedly terminated. * event `StateChanged(ITerminalConnection, IInspectable)` * (the `IInspectable` argument is recommended and required for a typed event handler, but it will bear no payload.) * event `TerminalDisconnected` will be removed, as it is replaced by `StateChanged` * **NOTE**: A conforming implementation MUST treat states as a directed acyclic graph. States MUST NOT be transitioned in reverse. * A helper class may be provided for managing state transitions. ### `TerminalControl` Changes * As the decision as to whether to close a terminal control hosting a connection that has transitioned into a terminal state will be made by the application, the unexpressive `Close` event will be removed and replaced with a `ConnectionStateChanged` event. * `event ConnectionStateChanged(TerminalControl, IInspectable)` event will project its connection's `StateChanged` event. * TerminalControl's new `ConnectionState` will project its connection's `State`. * (this is indicated for an eventual data binding; see Future Considerations.) ### Application and Settings 1. The existing `closeOnExit` profile key will be replaced with an enumerated string key supporting the following values (behaviors): * `always` - a tab or pane hosting this profile will always be closed when the launched connection reaches a terminal state. * `graceful` - a tab or pane hosting this profile will be closed if and only if the launched connection reaches the `Closed` terminal state. * `never` - a tab or pane hosting this profile will not automatically close. * See the Compatibility section for information on the legacy settings transition. * **The new default value for `closeOnExit` will be `graceful`.** 2. `Pane` will remain responsible for making the final determination as to whether it is closed based on the settings of the profile it is hosting. ## UI/UX Design * The existing `ITerminalConnection` implementations will be augmented to print out interesting and useful status information when they transition into a `Closed` or `Failed` state. * Example (ConPTY connection) * The pseudoconsole cannot be opened, or the process fails to launch.
`[failed to spawn 'thing': 0x80070002]`, transition to `Failed`. * The process exited unexpectedly.
`[process exited with code 300]`, transition to `Failed`. * The process exited normally.
`[process exited with code 0]`, transition to `Closed`. * _The final message will always be printed_ regardless of user configuration. * If the user's settings specify `closeOnExit: never/false`, the terminal hosting the connection will never be automatically closed. The message will remain on-screen. * If the user's settings specify `closeOnExit: graceful/true`, the terminal hosting the connection _will_ automatically be closed if the connection's state is `Closed`. A connection in the `Failed` state will not be closed, and the message will remain on-screen. * If the user's settings specify `closeOnExit: always`, the terminal hosting the connection will be closed. The message will not be seen. ## Capabilities ### Accessibility This will give users of all technologies a way to know when their shell has failed to launch or has exited with an unexpected status code. ### Security There will be no impact to security. ### Reliability Windows Terminal will no longer immediately terminate on startup if the user's shell doesn't exist. ### Compatibility There is an existing `closeOnExit` _boolean_ key that a user may have configured in profiles.json. The boolean values should map as follows: * `true` -> `graceful` * `false` -> `never` This will make for a clean transition to Windows Terminal's sane new defaults. ### Performance, Power, and Efficiency ## Potential Issues There will be no impact to Performance, Power or Efficiency. ## Future considerations * Eventually, we may want to implement a feature like "only close on graceful exit if the shell was running for more than X seconds". This puts us in a better position to do that, as we can detect graceful and clumsy exits more readily. * (potential suggestion: `{ "closeOnExit": "10s" }` * The enumerator values for transitioning connection states will be useful for connections that require internet access. * Since the connection states are exposed through `TerminalControl`, they should be able to be data-bound to other Xaml elements. This can be used to provide discrete UI states for terminal controls, panes or tabs _hosting_ terminal controls. * Example: a tab hosting a terminal control whose connection has been broken MAY display a red border. * Example: an inactive tab that reaches the `Connected` state MAY flash to indicate that it is ready.