diff --git a/Directory.Packages.props b/Directory.Packages.props
index b3d4691..2911b07 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -21,5 +21,6 @@
+
diff --git a/docs/advanced-vt-protocols.md b/docs/advanced-vt-protocols.md
index ffa2a65..8b5f98e 100644
--- a/docs/advanced-vt-protocols.md
+++ b/docs/advanced-vt-protocols.md
@@ -1,12 +1,11 @@
# Advanced VT protocols
> [!NOTE]
-> The public out-of-process Windows ConPTY may filter DCS payloads before they
-> reach a terminal client on some Windows builds, and APC sequences (kitty
-> graphics) can be affected the same way. The Core parser and renderer
-> support Sixel and kitty graphics when a connection transports those bytes
-> unchanged (for example, remote/Azure transports); local ConPTY support is
-> limited by the installed Windows pseudoconsole implementation.
+> Windows local sessions use the bundled Microsoft ConPTY runtime, including
+> `OpenConsole.exe`, rather than the OS copy that filters Sixel on some Windows
+> builds. With the built-in engine, Sixel producers such as `Get-XKCD -Show`
+> can render inline images. Other transports must likewise preserve graphics
+> payloads; the optional Ghostty engine does not expose image resources.
`Devolutions.Terminal.Core` parses advanced string protocols without depending on Avalonia,
Skia, Win32, or an image codec. It exposes decoded Sixel pixels and bounded
@@ -36,6 +35,9 @@ terminator split across input chunks. BEL does not terminate DCS. An `ESC`
followed by anything other than `\` aborts the DCS and starts the new escape
sequence.
+DECSDM defaults to reset (scrolling mode): Sixel images anchor at the cursor
+unless the application explicitly selects display mode with `CSI ? 80 h`.
+
## Limits
Limits are public constants on `TerminalImageLimits` and `VtResourceLimits`.
diff --git a/docs/release.md b/docs/release.md
index c7b96d2..16acc80 100644
--- a/docs/release.md
+++ b/docs/release.md
@@ -12,7 +12,17 @@ Commands are relative to the repository root.
- WinApp CLI 0.6.0 for MSIX creation and validation
- A trusted code-signing certificate for distributable MSIX artifacts
-Windows local sessions use ConPTY. Linux and macOS local sessions use the
+Windows local sessions use the pinned `Microsoft.Windows.Console.ConPTY` NuGet
+runtime. Ship `conpty.dll` and the architecture-specific `OpenConsole.exe`
+subdirectories together: without its host, ConPTY can fall back to the OS copy
+and silently lose Sixel output. `ConPtyHost.targets` supplies these files for
+RID-less builds, RID-specific publishes, and Control NuGet consumers, including
+the ARM64 host required by x64 processes running under emulation. Because
+`conpty.dll` lands under `runtimes\\native` for RID-less builds but at the
+output root once a RID is applied, the hosts are emitted in both layouts; a RID
+does not reliably flow to referenced projects, so the placement cannot depend on
+it.
+Linux and macOS local sessions use the
bundled `forkpty` relay. The Avalonia shell, settings, renderer, and terminal
engines are shared.
@@ -400,9 +410,9 @@ that `Control` needs, or a project that needs direct access to
## Platform constraints
-- Public out-of-process ConPTY can filter or alter DCS/APC payloads on some
- Windows builds. Sixel works when the selected connection passes DCS bytes
- through unchanged.
+- The OS-provided ConPTY can filter graphics payloads on some Windows builds.
+ Local sessions use the bundled runtime to preserve Sixel; remote connections
+ must also transport image sequences unchanged.
- Avalonia 12 exposes the terminal as a readable UIA Document/Value provider,
but does not provide a public bridge for native UIA TextPattern/TextPattern2
or LiveSetting events. Managed ranges and visible notification text remain
diff --git a/src/Devolutions.Terminal.Connection/ConPtyConnection.cs b/src/Devolutions.Terminal.Connection/ConPtyConnection.cs
index ff097e9..d4d98da 100644
--- a/src/Devolutions.Terminal.Connection/ConPtyConnection.cs
+++ b/src/Devolutions.Terminal.Connection/ConPtyConnection.cs
@@ -158,7 +158,7 @@ public void Resize(int columns, int rows)
if (pseudoConsole is not null && !pseudoConsole.IsInvalid && !pseudoConsole.IsClosed)
{
var size = new Kernel32.Coord { X = (short)validatedColumns, Y = (short)validatedRows };
- var hr = Kernel32.ResizePseudoConsole(pseudoConsole, size);
+ var hr = ConPty.ResizePseudoConsole(pseudoConsole, size);
if (hr != 0)
{
Marshal.ThrowExceptionForHR(hr);
@@ -225,7 +225,7 @@ private void StartCore(TerminalLaunchOptions options, CancellationToken cancella
X = (short)options.Columns,
Y = (short)options.Rows,
};
- var hr = Kernel32.CreatePseudoConsole(size, inputRead, outputWrite, 0, out var pseudoConsoleValue);
+ var hr = ConPty.CreatePseudoConsole(size, inputRead, outputWrite, 0, out var pseudoConsoleValue);
if (hr != 0)
{
Marshal.ThrowExceptionForHR(hr);
diff --git a/src/Devolutions.Terminal.Connection/ConPtyHost.targets b/src/Devolutions.Terminal.Connection/ConPtyHost.targets
new file mode 100644
index 0000000..dc8c81d
--- /dev/null
+++ b/src/Devolutions.Terminal.Connection/ConPtyHost.targets
@@ -0,0 +1,34 @@
+
+
+ $(MSBuildThisFileDirectory)..\tools\conpty
+
+
+
+
+ <_ConPtyArch Include="x86;x64;arm64" />
+ <_ConPtyRuntime Include="win-x86;win-x64;win-arm64" Condition="'$(RuntimeIdentifier)' == ''" />
+
+
+
+
+
+
+
+
diff --git a/src/Devolutions.Terminal.Connection/Devolutions.Terminal.Connection.csproj b/src/Devolutions.Terminal.Connection/Devolutions.Terminal.Connection.csproj
index d22e93a..7aa09bb 100644
--- a/src/Devolutions.Terminal.Connection/Devolutions.Terminal.Connection.csproj
+++ b/src/Devolutions.Terminal.Connection/Devolutions.Terminal.Connection.csproj
@@ -7,6 +7,11 @@
false
+
+
+ $(PkgMicrosoft_Windows_Console_ConPTY)\build\native\runtimes
+
+
diff --git a/src/Devolutions.Terminal.Connection/Native/ConPty.cs b/src/Devolutions.Terminal.Connection/Native/ConPty.cs
new file mode 100644
index 0000000..46a987f
--- /dev/null
+++ b/src/Devolutions.Terminal.Connection/Native/ConPty.cs
@@ -0,0 +1,18 @@
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using Microsoft.Win32.SafeHandles;
+
+namespace Devolutions.Terminal.Connection.Native;
+
+[SupportedOSPlatform("windows")]
+internal static partial class ConPty
+{
+ [LibraryImport("conpty.dll", EntryPoint = "ConptyCreatePseudoConsole")]
+ internal static partial int CreatePseudoConsole(Kernel32.Coord size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out nint phPC);
+
+ [LibraryImport("conpty.dll", EntryPoint = "ConptyResizePseudoConsole")]
+ internal static partial int ResizePseudoConsole(SafePseudoConsoleHandle hPC, Kernel32.Coord size);
+
+ [LibraryImport("conpty.dll", EntryPoint = "ConptyClosePseudoConsole")]
+ internal static partial void ClosePseudoConsole(nint hPC);
+}
diff --git a/src/Devolutions.Terminal.Connection/Native/Kernel32.cs b/src/Devolutions.Terminal.Connection/Native/Kernel32.cs
index e325e59..e66fccb 100644
--- a/src/Devolutions.Terminal.Connection/Native/Kernel32.cs
+++ b/src/Devolutions.Terminal.Connection/Native/Kernel32.cs
@@ -68,15 +68,6 @@ internal struct ProcessInformation
public int dwThreadId;
}
- [LibraryImport("kernel32.dll", SetLastError = true)]
- internal static partial int CreatePseudoConsole(Coord size, SafeFileHandle hInput, SafeFileHandle hOutput, uint dwFlags, out nint phPC);
-
- [LibraryImport("kernel32.dll", SetLastError = true)]
- internal static partial int ResizePseudoConsole(SafePseudoConsoleHandle hPC, Coord size);
-
- [LibraryImport("kernel32.dll", SetLastError = true)]
- internal static partial void ClosePseudoConsole(nint hPC);
-
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, nint lpPipeAttributes, uint nSize);
diff --git a/src/Devolutions.Terminal.Connection/Native/SafeHandles.cs b/src/Devolutions.Terminal.Connection/Native/SafeHandles.cs
index 11320c3..75d6dd4 100644
--- a/src/Devolutions.Terminal.Connection/Native/SafeHandles.cs
+++ b/src/Devolutions.Terminal.Connection/Native/SafeHandles.cs
@@ -21,7 +21,7 @@ internal SafePseudoConsoleHandle(nint handle)
protected override bool ReleaseHandle()
{
- Kernel32.ClosePseudoConsole(handle);
+ ConPty.ClosePseudoConsole(handle);
return true;
}
}
diff --git a/src/Devolutions.Terminal.Connection/THIRD-PARTY-NOTICES-CONPTY.txt b/src/Devolutions.Terminal.Connection/THIRD-PARTY-NOTICES-CONPTY.txt
new file mode 100644
index 0000000..cdec909
--- /dev/null
+++ b/src/Devolutions.Terminal.Connection/THIRD-PARTY-NOTICES-CONPTY.txt
@@ -0,0 +1,24 @@
+Microsoft Windows Console ConPTY
+https://github.com/microsoft/terminal
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/Devolutions.Terminal.Control/Devolutions.Terminal.Control.csproj b/src/Devolutions.Terminal.Control/Devolutions.Terminal.Control.csproj
index 3e58d76..1e449fb 100644
--- a/src/Devolutions.Terminal.Control/Devolutions.Terminal.Control.csproj
+++ b/src/Devolutions.Terminal.Control/Devolutions.Terminal.Control.csproj
@@ -36,6 +36,7 @@
+