Repository: microsoft/terminal (language: cpp)

Issue:
Support querying the colors via OSC
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

OSC 4, 10, 11 and friends, with a `?` instead of a color, respond with the current value of that color in xterm, vte and quite a few other terminals, always using the `rgb:RRRR/GGGG/BBBB` notation, and the same terminator (ST vs. BEL) as in the query.

Examples:

    echo -ne '\e]4;1;?\e\\'; cat

    echo -ne '\e]11;?\a'; cat

This allows apps (e.g. vim) to automatically pick a color scheme depending on whether the user has a dark-on-bright on bright-on-dark color scheme. I don't know if it's actually being used in practice, though :D



Discussion / hints:
I should probably raise a separate issue for this, but I just wanted to note that there are standard DEC sequences for querying the color table (and setting it for that matter) which we may want to support as well.
Note that VTE responds with `rgb:RRRR/GGGG/BBBB` format. But iTerm2 responds with `rgb:RR/GG/BB` format. I don't have a daily Linux machine so I don't know what xterm is using. 
I just tested urxvt and it responded with `rgba:0000/0000/0000/f332` :)
Well that's awkward. So I have to assume that XTerm has support for `rgba:` (and at the same time,`rgbi`, according to @egmontkob) ?

Personally I'd prefer `rgb:RR/GG/BB` since it's close to `#RRGGBB` which is everywhere on the web.

From the [XParseColor docs](https://www.xfree86.org/4.8.0/XLookupColor.3.html#toc4) (via the [Xterm docs for OSC 4](http://www.xfree86.org/4.5.0/ctlseqs.html)), the RR vs RRR vs RRRR is about the bit-depth of the value. So `rgb:RR/GG/BB` is 24-bit colour (8/8/8/0), and anything longer is really Wide Colour Gamut, and should be scaled down. (Hence things like `#302351` being `rgba:3000/2300/5100/ee00` in [urxvt tips-and-tricks](https://wiki.archlinux.org/index.php/Rxvt-unicode/Tips_and_tricks), assuming a linear tone-mapping function.

That said, I guess we don't have native WCG support in Terminal right now, and I expect we'd return the 24-bit colours we are working with, not the tone-mapped resulting colours if we happen to be running on a WCG-based display.

So I also support `rgb:RR/GG/BB` as long as we're working from a 24-bit palette.

I'm guessing `rgba:` is a common extension for alpha... It's not in the `XParseColor` docs, and I remember back in the day, the best reason to use rxvt over xterm was transparent backgrounds... Perhaps `rgba:` is (or was) an rxvt-specific extension?

Looks like it was [rxvt-specific](https://github.com/exg/rxvt-unicode/blob/master/src/rxvttoolkit.C#L841-L860), and unlike `rgb:`, `rgba:` is [specified](https://linux.die.net/man/1/urxvt) to *always* have 16 bits _per channel_. Someone was very forward-thinking in the 90's... Or lazy when writing a string parser.
I've tested the color table queries on 9 different terminal emulators - every one of them (including XTerm , Gnome Terminal, Konsole, and Rxvt) reported the colors as `rgb:rrrr/gggg/bbbb` (typically scaled up from the internal 24-bit representation). I see in the Rxvt source that there is a build option that will make it return `rgba`, but that didn't seem to be active on my test system - perhaps it's OS dependent? In any event, I'd think the `rgb:rrrr/gggg/bbbb` format is almost certainly the right choice if we want to be compatible with what most people are doing.

Regarding the other color formats, XTerm does support rgbi and the various cie variants, but the report format is always rgb.

Out of sheer curiosity, what is the reason that makes iTerm2 the weirdest kid in town?

CC @gnachman 
I believe iTerm2 has an option to choose between reporting as `rgb:rrrr/gggg/bbbb` and `rgb:rr/gg/bb`. They got it wrong initially, and then were forced to add the option to support the correct format because it was breaking in vim.
I used 8 bit codes originally for reasons I no longer recall. I was made aware of the issue in this post: https://groups.google.com/g/iterm2-discuss/c/xXzeBY2aRjo/m/zHBZreQWCQAJ

Commit 5c5785f3632b8e90dd69f458411a8b8b17aa0599 changed the default to 16 bits. It is now enabled in 3.4.0 betas, which will exit beta when Big Sur is released.
To be clear, per the spec, _both_ `rgb:rr/gg/bb` and `rgb:rrrr/gggg/bbbb` are valid. So is `rgb:rrr/ggg/bbb`, and a few other options. The item2-discuss link from @gnachman shows that it was a bug in vim before 8.1, that it incorrectly implemented the `XParseColor`-compatible colour parser.

That said, the commonality of vim, particularly older vim, means it's probably worth being bug-compatible here from the start, even though it's being overly-precise.

My reading of the Xterm docs for OSC 4 is that if you call `OSC 4 XXXX`, then `OSC 4 ?` should be returning precisely that `XXXX`, but I can see how it can be read to allow returning some numerically-equivalent values, i.e. 48-bit RGB, even if the `XXX` was originally `CIELab:...`.
> My reading of the Xterm docs for OSC 4 is that if you call `OSC 4 XXXX`, then `OSC 4 ?` should be returning precisely that `XXXX`

Yeah, it certainly does read like that. But in practice the spec is defined by the reference implementation, which in this case is XTerm. If you want to be "spec compliant" you need to match whatever XTerm does. And when you think about it, it makes sense for your terminal to be liberal in what it accepts (i.e. supporting as many formats as possible), but conservative in what it produces as a response (you can't reasonably expect every application that queries the palette to understand all the different formats).
That's fair. And given that (for an older version, I didn't check anything newer) xterm `OSC 4 n ?` produces [`XQueryColor` formatted as `rgb:%04x/%04x/%04x`](https://github.com/ThomasDickey/old-xterm/blob/master/misc.c#L1334-L1351), that seems a safe thing to do.

> you can't reasonably expect every application that queries the palette to understand all the different formats

I agree in general. In this case however, because it's not "all the different formats", it's a format defined by a specific API (`XParseColor`), I would absolutely expect *of myself* that a parser was prepared to handle those documented formats. Apps on X11 of course have it easy, because they can just _use_ `XParseColor`, but I'm unaware off-hand of anyone knocking together an X11-independent implementation of the API, which is a shame, but I guess the OSC colour codes are the only, highly-specific use-case for such a thing.

It may not be _kind_ to specify an API behaviour in terms of the API it's implemented with underneath, but that is what we have here. (Edit: If anyone's looking for the implementation side of `XParseColor`, the colour space parsing is farmed out to functions pointed to by the [`struct XcmsColorSpace` instances](https://github.com/freedesktop/xorg-libX11/blob/d127217f26df1bf7566c1f372d8b5329a06754ea/src/xcms/Cv.h#L16-L23), and [libXRender separately defines `rgba:`](https://github.com/freedesktop/xorg-libXrender/blob/bce0618839fc33f44edd8b5498b8e33d167806ff/src/Color.c#L33-L74) in `XRenderParseColor`).

That said, Microsoft Terminal [cannot _consume_ `rgb:rrrr/gggg/bbbb` for OSC 4 right now](https://github.com/microsoft/terminal/blob/88d1527985123aea66537447241e3fa989fbf755/src/terminal/parser/OutputStateMachineEngine.cpp#L1539-L1548), so we probably need to fix that, or we end up in the situation where we cannot roundtrip our `OSC 4 n ?` back to `OSC 4 x <string we saved earlier>`.

Our [local implementation of `XParseColor`](https://github.com/microsoft/terminal/blob/88d1527985123aea66537447241e3fa989fbf755/src/terminal/parser/OutputStateMachineEngine.cpp#L1409-L1420) could do with some love too.
I’m working on it. See #7578 if you have time 😄 
Hello, I am also bumping into this issue. Vim calls [OSC 11;?](https://github.com/vim/vim/blob/master/src/term.c#L901) to fetch the terminal's background color when determining what color scheme to use; but because WT does not support fetching this OSC value (only setting it) vim defaults to a color scheme for light terminals.

Is my understanding correct that the outlines of what needs to be done have been laid out but that this feature is not actively under development?
Yep, that's about the long and short of it. We've already got code elsewhere in the parser for replying to queries, so that can be done. We'd just need conpty to pass through the query through to the connected terminal, and have the terminal reply. Probably wouldn't be that hard, but it's just fairly low down on the backlog currently. We'd happily accept a community contribution though!
Ahh, ok, understood. Thank you for the summary! If I have some down time in the next few weeks I will start poking around and putting a PR together.
@mboruta1  I was working on this area previously. The PRs related are #7578 and #8999. It might help you with the “poking around” of the codebase 😃
Thank you @skyline75489 :)
I, too, would love to see this.

I'm cycling colors with a python script, and can't start the color cycle at the existing color without knowing what it is.

Re-creating an old dos program called glow.exe, basically.

Fix patch:
diff --git a/.github/actions/spelling/patterns/patterns.txt b/.github/actions/spelling/patterns/patterns.txt
index 0089073a434..39fadfec281 100644
--- a/.github/actions/spelling/patterns/patterns.txt
+++ b/.github/actions/spelling/patterns/patterns.txt
@@ -22,6 +22,7 @@ vcvars\w*
 ROY\sG\.\sBIV
 !(?:(?i)ESC)!\[
 !(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
+(?i)rgb:[a-z0-9]{2,4}/[a-z0-9]{2,4}/[a-z0-9]{2,4}
 
 # SSE intrinsics like "_mm_subs_epu16"
 \b_mm(?:|256|512)_\w+\b
diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp
index bf4d2cc5bd5..514c8b0c4d9 100644
--- a/src/terminal/adapter/ITermDispatch.hpp
+++ b/src/terminal/adapter/ITermDispatch.hpp
@@ -76,9 +76,10 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
     virtual bool BackwardsTab(const VTInt numTabs) = 0; // CBT
     virtual bool TabClear(const DispatchTypes::TabClearType clearType) = 0; // TBC
     virtual bool TabSet(const VTParameter setType) = 0; // DECST8C
-    virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) = 0; // OSCColorTable
-    virtual bool SetDefaultForeground(const DWORD color) = 0; // OSCDefaultForeground
-    virtual bool SetDefaultBackground(const DWORD color) = 0; // OSCDefaultBackground
+    virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) = 0; // OSCSetColorTable
+    virtual bool RequestColorTableEntry(const size_t tableIndex) = 0; // OSCGetColorTable
+    virtual bool SetXtermColorResource(const size_t resource, const DWORD color) = 0; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor
+    virtual bool RequestXtermColorResource(const size_t resource) = 0; // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor
     virtual bool AssignColor(const DispatchTypes::ColorItem item, const VTInt fgIndex, const VTInt bgIndex) = 0; // DECAC
 
     virtual bool EraseInDisplay(const DispatchTypes::EraseType eraseType) = 0; // ED
@@ -128,7 +129,6 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
     virtual bool ScreenAlignmentPattern() = 0; // DECALN
 
     virtual bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR
-    virtual bool SetCursorColor(const COLORREF color) = 0; // OSCSetCursorColor, OSCResetCursorColor
 
     virtual bool SetClipboard(wil::zwstring_view content) = 0; // OSCSetClipboard
 
diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp
index 3aca2abfdf9..1c84e939ffb 100644
--- a/src/terminal/adapter/adaptDispatch.cpp
+++ b/src/terminal/adapter/adaptDispatch.cpp
@@ -18,6 +18,25 @@ using namespace Microsoft::Console::VirtualTerminal;
 
 static constexpr std::wstring_view whitespace{ L" " };
 
+struct XtermResourceColorTableEntry
+{
+    int ColorTableIndex;
+    int AliasIndex;
+};
+
+static constexpr std::array<XtermResourceColorTableEntry, 10> XtermResourceColorTableMappings{ {
+    /* 10 */ { TextColor::DEFAULT_FOREGROUND, static_cast<int>(ColorAlias::DefaultForeground) },
+    /* 11 */ { TextColor::DEFAULT_BACKGROUND, static_cast<int>(ColorAlias::DefaultBackground) },
+    /* 12 */ { TextColor::CURSOR_COLOR, -1 },
+    /* 13 */ { -1, -1 },
+    /* 14 */ { -1, -1 },
+    /* 15 */ { -1, -1 },
+    /* 16 */ { -1, -1 },
+    /* 17 */ { -1, -1 },
+    /* 18 */ { -1, -1 },
+    /* 19 */ { -1, -1 },
+} };
+
 AdaptDispatch::AdaptDispatch(ITerminalApi& api, Renderer* renderer, RenderSettings& renderSettings, TerminalInput& terminalInput) noexcept :
     _api{ api },
     _renderer{ renderer },
@@ -3439,18 +3458,6 @@ bool AdaptDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle)
     return true;
 }
 
-// Method Description:
-// - Sets a single entry of the colortable to a new value
-// Arguments:
-// - tableIndex: The VT color table index
-// - dwColor: The new RGB color value to use.
-// Return Value:
-// True if handled successfully. False otherwise.
-bool AdaptDispatch::SetCursorColor(const COLORREF cursorColor)
-{
-    return SetColorTableEntry(TextColor::CURSOR_COLOR, cursorColor);
-}
-
 // Routine Description:
 // - OSC Copy to Clipboard
 // Arguments:
@@ -3491,28 +3498,69 @@ bool AdaptDispatch::SetColorTableEntry(const size_t tableIndex, const DWORD dwCo
     return true;
 }
 
+bool AdaptDispatch::RequestColorTableEntry(const size_t tableIndex)
+{
+    const auto color = _renderSettings.GetColorTableEntry(tableIndex);
+    if (color != INVALID_COLOR)
+    {
+        const til::color c{ color };
+        // Scale values up to match xterm's 16-bit color report format.
+        _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033]4;{};rgb:{:04x}/{:04x}/{:04x}\033\\"), tableIndex, c.r * 0x0101, c.g * 0x0101, c.b * 0x0101));
+    }
+
+    return true;
+}
+
 // Method Description:
-// - Sets the default foreground color to a new value
-// Arguments:
-// - dwColor: The new RGB color value to use, as a COLORREF, format 0x00BBGGRR.
+// - Sets one Xterm Color Resource such as Default Foreground, Background, Cursor
 // Return Value:
 // True if handled successfully. False otherwise.
-bool AdaptDispatch::SetDefaultForeground(const DWORD dwColor)
+bool AdaptDispatch::SetXtermColorResource(const size_t resource, const DWORD color)
 {
-    _renderSettings.SetColorAliasIndex(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND);
-    return SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, dwColor);
+    assert(resource >= 10);
+    const auto mappingIndex = resource - 10;
+    const auto& oscMapping = XtermResourceColorTableMappings.at(mappingIndex);
+    if (oscMapping.ColorTableIndex > 0)
+    {
+        if (oscMapping.AliasIndex >= 0) [[unlikely]]
+        {
+            // If this color change applies to an aliased color, point the alias at the new color
+            _renderSettings.SetColorAliasIndex(static_cast<ColorAlias>(oscMapping.AliasIndex), oscMapping.ColorTableIndex);
+        }
+        return SetColorTableEntry(oscMapping.ColorTableIndex, color);
+    }
+
+    return true;
 }
 
 // Method Description:
-// - Sets the default background color to a new value
-// Arguments:
-// - dwColor: The new RGB color value to use, as a COLORREF, format 0x00BBGGRR.
+// - Reports the value of one Xterm Color Resource, if it is set.
 // Return Value:
 // True if handled successfully. False otherwise.
-bool AdaptDispatch::SetDefaultBackground(const DWORD dwColor)
+bool AdaptDispatch::RequestXtermColorResource(const size_t resource)
 {
-    _renderSettings.SetColorAliasIndex(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND);
-    return SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, dwColor);
+    assert(resource >= 10);
+    const auto mappingIndex = resource - 10;
+    const auto& oscMapping = XtermResourceColorTableMappings.at(mappingIndex);
+    if (oscMapping.ColorTableIndex > 0)
+    {
+        size_t finalColorIndex = oscMapping.ColorTableIndex;
+
+        if (oscMapping.AliasIndex >= 0) [[unlikely]]
+        {
+            finalColorIndex = _renderSettings.GetColorAliasIndex(static_cast<ColorAlias>(oscMapping.AliasIndex));
+        }
+
+        const auto color = _renderSettings.GetColorTableEntry(finalColorIndex);
+        if (color != INVALID_COLOR)
+        {
+            const til::color c{ color };
+            // Scale values up to match xterm's 16-bit color report format.
+            _api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033]{};rgb:{:04x}/{:04x}/{:04x}\033\\"), resource, c.r * 0x0101, c.g * 0x0101, c.b * 0x0101));
+        }
+    }
+
+    return true;
 }
 
 // Method Description:
diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp
index cba0273665d..ef5636be1e2 100644
--- a/src/terminal/adapter/adaptDispatch.hpp
+++ b/src/terminal/adapter/adaptDispatch.hpp
@@ -126,14 +126,14 @@ namespace Microsoft::Console::VirtualTerminal
         bool HardReset() override; // RIS
         bool ScreenAlignmentPattern() override; // DECALN
         bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
-        bool SetCursorColor(const COLORREF cursorColor) override;
 
         bool SetClipboard(const wil::zwstring_view content) override; // OSCSetClipboard
 
         bool SetColorTableEntry(const size_t tableIndex,
-                                const DWORD color) override; // OSCColorTable
-        bool SetDefaultForeground(const DWORD color) override; // OSCDefaultForeground
-        bool SetDefaultBackground(const DWORD color) override; // OSCDefaultBackground
+                                const DWORD color) override; // OSCSetColorTable
+        bool RequestColorTableEntry(const size_t tableIndex) override; // OSCGetColorTable
+        bool SetXtermColorResource(const size_t resource, const DWORD color) override; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor
+        bool RequestXtermColorResource(const size_t resource) override; // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor
         bool AssignColor(const DispatchTypes::ColorItem item, const VTInt fgIndex, const VTInt bgIndex) override; // DECAC
 
         bool WindowManipulation(const DispatchTypes::WindowManipulationType function,
diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp
index 607089545bf..5d256928712 100644
--- a/src/terminal/adapter/termDispatch.hpp
+++ b/src/terminal/adapter/termDispatch.hpp
@@ -69,9 +69,10 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons
     bool BackwardsTab(const VTInt /*numTabs*/) override { return false; } // CBT
     bool TabClear(const DispatchTypes::TabClearType /*clearType*/) override { return false; } // TBC
     bool TabSet(const VTParameter /*setType*/) override { return false; } // DECST8C
-    bool SetColorTableEntry(const size_t /*tableIndex*/, const DWORD /*color*/) override { return false; } // OSCColorTable
-    bool SetDefaultForeground(const DWORD /*color*/) override { return false; } // OSCDefaultForeground
-    bool SetDefaultBackground(const DWORD /*color*/) override { return false; } // OSCDefaultBackground
+    bool SetColorTableEntry(const size_t /*tableIndex*/, const DWORD /*color*/) override { return false; } // OSCSetColorTable
+    bool RequestColorTableEntry(const size_t /*tableIndex*/) override { return false; } // OSCGetColorTable
+    bool SetXtermColorResource(const size_t /*resource*/, const DWORD /*color*/) override { return false; } // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor
+    bool RequestXtermColorResource(const size_t /*resource*/) override { return false; } // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor
     bool AssignColor(const DispatchTypes::ColorItem /*item*/, const VTInt /*fgIndex*/, const VTInt /*bgIndex*/) override { return false; } // DECAC
 
     bool EraseInDisplay(const DispatchTypes::EraseType /* eraseType*/) override { return false; } // ED
@@ -121,7 +122,6 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons
     bool ScreenAlignmentPattern() override { return false; } // DECALN
 
     bool SetCursorStyle(const DispatchTypes::CursorStyle /*cursorStyle*/) override { return false; } // DECSCUSR
-    bool SetCursorColor(const COLORREF /*color*/) override { return false; } // OSCSetCursorColor, OSCResetCursorColor
 
     bool SetClipboard(wil::zwstring_view /*content*/) override { return false; } // OscSetClipboard
 
diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp
index edd13ddab01..7d571086edd 100644
--- a/src/terminal/adapter/ut_adapter/adapterTest.cpp
+++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp
@@ -2398,6 +2398,232 @@ class AdapterTest
         _testGetSet->ValidateInputEvent(expectedResponse.c_str());
     }
 
+    TEST_METHOD(Osc4ColorPaletteReportTests)
+    {
+        _testGetSet->PrepData();
+
+        // The colors below use the same VT525 colors as the other color table report tests.
+        auto& renderSettings = _testGetSet->_renderer._renderSettings;
+        renderSettings.SetColorTableEntry(0, RGB(0, 0, 0));
+        renderSettings.SetColorTableEntry(1, RGB(204, 36, 36));
+        renderSettings.SetColorTableEntry(2, RGB(51, 204, 51));
+        renderSettings.SetColorTableEntry(3, RGB(204, 204, 51));
+        renderSettings.SetColorTableEntry(4, RGB(51, 51, 204));
+        renderSettings.SetColorTableEntry(5, RGB(204, 51, 204));
+        renderSettings.SetColorTableEntry(6, RGB(51, 204, 204));
+        renderSettings.SetColorTableEntry(7, RGB(120, 120, 120));
+        renderSettings.SetColorTableEntry(8, RGB(69, 69, 69));
+        renderSettings.SetColorTableEntry(9, RGB(255, 0, 0));
+        renderSettings.SetColorTableEntry(10, RGB(0, 255, 0));
+        renderSettings.SetColorTableEntry(11, RGB(255, 255, 0));
+        renderSettings.SetColorTableEntry(12, RGB(0, 0, 255));
+        renderSettings.SetColorTableEntry(13, RGB(255, 0, 255));
+        renderSettings.SetColorTableEntry(14, RGB(0, 255, 255));
+        renderSettings.SetColorTableEntry(15, RGB(255, 255, 255));
+        for (size_t i = 16; i < TextColor::TABLE_SIZE; i++)
+        {
+            renderSettings.SetColorTableEntry(i, RGB(0, 0, 0));
+        }
+
+        // Dynamic color reports begin with an OSC, parameter 4, another parameter for the index, and end with ST.
+        const auto OSC = L"\033]";
+        const auto ST = L"\033\\";
+
+        _pDispatch->RequestColorTableEntry(0);
+        std::wstring expectedResponse = OSC;
+        expectedResponse += L"4;0;rgb:0000/0000/0000";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(1);
+        expectedResponse = OSC;
+        expectedResponse += L"4;1;rgb:cccc/2424/2424";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(2);
+        expectedResponse = OSC;
+        expectedResponse += L"4;2;rgb:3333/cccc/3333";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(3);
+        expectedResponse = OSC;
+        expectedResponse += L"4;3;rgb:cccc/cccc/3333";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(4);
+        expectedResponse = OSC;
+        expectedResponse += L"4;4;rgb:3333/3333/cccc";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(5);
+        expectedResponse = OSC;
+        expectedResponse += L"4;5;rgb:cccc/3333/cccc";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(6);
+        expectedResponse = OSC;
+        expectedResponse += L"4;6;rgb:3333/cccc/cccc";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(7);
+        expectedResponse = OSC;
+        expectedResponse += L"4;7;rgb:7878/7878/7878";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(8);
+        expectedResponse = OSC;
+        expectedResponse += L"4;8;rgb:4545/4545/4545";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(9);
+        expectedResponse = OSC;
+        expectedResponse += L"4;9;rgb:ffff/0000/0000";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(10);
+        expectedResponse = OSC;
+        expectedResponse += L"4;10;rgb:0000/ffff/0000";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(11);
+        expectedResponse = OSC;
+        expectedResponse += L"4;11;rgb:ffff/ffff/0000";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(12);
+        expectedResponse = OSC;
+        expectedResponse += L"4;12;rgb:0000/0000/ffff";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(13);
+        expectedResponse = OSC;
+        expectedResponse += L"4;13;rgb:ffff/0000/ffff";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(14);
+        expectedResponse = OSC;
+        expectedResponse += L"4;14;rgb:0000/ffff/ffff";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        _pDispatch->RequestColorTableEntry(15);
+        expectedResponse = OSC;
+        expectedResponse += L"4;15;rgb:ffff/ffff/ffff";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+    }
+
+    TEST_METHOD(XtermColorResourceReportTests)
+    {
+        _testGetSet->PrepData();
+
+        // The colors below use the same VT525 colors as the other color table report tests.
+        auto& renderSettings = _testGetSet->_renderer._renderSettings;
+        renderSettings.SetColorTableEntry(0, RGB(0, 0, 0));
+        renderSettings.SetColorTableEntry(1, RGB(204, 36, 36));
+        renderSettings.SetColorTableEntry(2, RGB(51, 204, 51));
+        renderSettings.SetColorTableEntry(3, RGB(204, 204, 51));
+        renderSettings.SetColorTableEntry(4, RGB(51, 51, 204));
+        renderSettings.SetColorTableEntry(5, RGB(204, 51, 204));
+        renderSettings.SetColorTableEntry(6, RGB(51, 204, 204));
+        renderSettings.SetColorTableEntry(7, RGB(120, 120, 120));
+        renderSettings.SetColorTableEntry(8, RGB(69, 69, 69));
+        renderSettings.SetColorTableEntry(9, RGB(255, 0, 0));
+        renderSettings.SetColorTableEntry(10, RGB(0, 255, 0));
+        renderSettings.SetColorTableEntry(11, RGB(255, 255, 0));
+        renderSettings.SetColorTableEntry(12, RGB(0, 0, 255));
+        renderSettings.SetColorTableEntry(13, RGB(255, 0, 255));
+        renderSettings.SetColorTableEntry(14, RGB(0, 255, 255));
+        renderSettings.SetColorTableEntry(15, RGB(255, 255, 255));
+
+        renderSettings.SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, RGB(190, 190, 190));
+        renderSettings.SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, RGB(12, 12, 12));
+        renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, RGB(255, 0, 0));
+
+        // Dynamic color reports begin with an OSC, a parameter matching the requested value, and end with ST.
+        const auto OSC = L"\033]";
+        const auto ST = L"\033\\";
+
+        // Foreground mapped to DARK_WHITE
+        _pDispatch->RequestXtermColorResource(10);
+        std::wstring expectedResponse = OSC;
+        expectedResponse += L"10;rgb:7878/7878/7878";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        // Foreground mapped to independent foreground color
+        renderSettings.SetColorAliasIndex(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND);
+        _pDispatch->RequestXtermColorResource(10);
+        expectedResponse = OSC;
+        expectedResponse += L"10;rgb:bebe/bebe/bebe";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        // Background mapped to DARK_BLACK
+        _pDispatch->RequestXtermColorResource(11);
+        expectedResponse = OSC;
+        expectedResponse += L"11;rgb:0000/0000/0000";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        // Background mapped to independent background color
+        renderSettings.SetColorAliasIndex(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND);
+        _pDispatch->RequestXtermColorResource(11);
+        expectedResponse = OSC;
+        expectedResponse += L"11;rgb:0c0c/0c0c/0c0c";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        // Foreground and Background mapped to different indices (e.g. via DECAC)
+        {
+            _testGetSet->_response.clear(); // manually clear (since we aren't issuing a call that will empty it)
+            auto retentionScope = _testGetSet->EnableInputRetentionInScope();
+            renderSettings.SetColorAliasIndex(ColorAlias::DefaultForeground, TextColor::DARK_RED);
+            renderSettings.SetColorAliasIndex(ColorAlias::DefaultBackground, TextColor::BRIGHT_GREEN);
+            _pDispatch->RequestXtermColorResource(10);
+            _pDispatch->RequestXtermColorResource(11);
+            expectedResponse = OSC;
+            expectedResponse += L"10;rgb:cccc/2424/2424";
+            expectedResponse += ST;
+            expectedResponse += OSC;
+            expectedResponse += L"11;rgb:0000/ffff/0000";
+            expectedResponse += ST;
+            _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+        }
+
+        _pDispatch->RequestXtermColorResource(12);
+        expectedResponse = OSC;
+        expectedResponse += L"12;rgb:ffff/0000/0000";
+        expectedResponse += ST;
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        // Resource set to unrepresentable color
+        _testGetSet->_response.clear(); // manually clear (since we aren't issuing a call that will empty it)
+        renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, INVALID_COLOR);
+        _pDispatch->RequestXtermColorResource(12);
+        expectedResponse = L"";
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+
+        // Unsupported resource - no mapped color
+        _testGetSet->_response.clear();
+        _pDispatch->RequestXtermColorResource(13);
+        expectedResponse = L"";
+        _testGetSet->ValidateInputEvent(expectedResponse.c_str());
+    }
+
     TEST_METHOD(TabulationStopReportTests)
     {
         _testGetSet->PrepData();
diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp
index 645ff33a1dc..d98bfd98de2 100644
--- a/src/terminal/parser/InputStateMachineEngine.cpp
+++ b/src/terminal/parser/InputStateMachineEngine.cpp
@@ -550,8 +550,19 @@ bool InputStateMachineEngine::ActionIgnore() noexcept
 // - string - OSC string we've collected. NOT null terminated.
 // Return Value:
 // - true if we handled the dispatch.
-bool InputStateMachineEngine::ActionOscDispatch(const size_t /*parameter*/, const std::wstring_view /*string*/) noexcept
+bool InputStateMachineEngine::ActionOscDispatch(const size_t /*parameter*/, const std::wstring_view /*string*/)
 {
+    // Unlike ActionCsiDispatch, we are not checking whether the application has requested
+    // VT input.
+    // Our documentation states that VT reports generated by application requests will be
+    // sent regardless of the state of `ENABLE_VIRTUAL_TERMINAL_INPUT`. We can't easily do
+    // that for CSI reports because we may incidentally pass through non-response VT input;
+    // however, there should be no OSC on the input stream *except* for responses.
+    // It should be safe to pass all OSCs from the input stream through to the application.
+    if (_pfnFlushToInputQueue)
+    {
+        return _pfnFlushToInputQueue();
+    }
     return false;
 }
 
diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp
index 32159f6f947..a572ec41123 100644
--- a/src/terminal/parser/InputStateMachineEngine.hpp
+++ b/src/terminal/parser/InputStateMachineEngine.hpp
@@ -157,7 +157,7 @@ namespace Microsoft::Console::VirtualTerminal
 
         bool ActionIgnore() noexcept override;
 
-        bool ActionOscDispatch(const size_t parameter, const std::wstring_view string) noexcept override;
+        bool ActionOscDispatch(const size_t parameter, const std::wstring_view string) override;
 
         bool ActionSs3Dispatch(const wchar_t wch, const VTParameters parameters) override;
 
diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp
index 66c993ac06b..874f382642a 100644
--- a/src/terminal/parser/OutputStateMachineEngine.cpp
+++ b/src/terminal/parser/OutputStateMachineEngine.cpp
@@ -14,6 +14,8 @@
 using namespace Microsoft::Console;
 using namespace Microsoft::Console::VirtualTerminal;
 
+constexpr COLORREF COLOR_INQUIRY_COLOR = 0xfeffffff; // It's like INVALID_COLOR but special
+
 // takes ownership of pDispatch
 OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr<ITermDispatch> pDispatch) :
     _dispatch(std::move(pDispatch)),
@@ -788,7 +790,14 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
         {
             const auto tableIndex = til::at(tableIndexes, i);
             const auto rgb = til::at(colors, i);
-            success = success && _dispatch->SetColorTableEntry(tableIndex, rgb);
+            if (rgb == COLOR_INQUIRY_COLOR)
+            {
+                success = success && _dispatch->RequestColorTableEntry(tableIndex);
+            }
+            else
+            {
+                success = success && _dispatch->SetColorTableEntry(tableIndex, rgb);
+            }
         }
         break;
     }
@@ -800,40 +809,18 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
         success = _GetOscSetColor(string, colors);
         if (success)
         {
-            auto commandIndex = parameter;
-            size_t colorIndex = 0;
-
-            if (commandIndex == OscActionCodes::SetForegroundColor && colors.size() > colorIndex)
+            auto resource = parameter;
+            for (auto&& color : colors)
             {
-                const auto color = til::at(colors, colorIndex);
-                if (color != INVALID_COLOR)
+                if (color == COLOR_INQUIRY_COLOR)
                 {
-                    success = success && _dispatch->SetDefaultForeground(color);
+                    success = success && _dispatch->RequestXtermColorResource(resource);
                 }
-                commandIndex++;
-                colorIndex++;
-            }
-
-            if (commandIndex == OscActionCodes::SetBackgroundColor && colors.size() > colorIndex)
-            {
-                const auto color = til::at(colors, colorIndex);
-                if (color != INVALID_COLOR)
-                {
-                    success = success && _dispatch->SetDefaultBackground(color);
-                }
-                commandIndex++;
-                colorIndex++;
-            }
-
-            if (commandIndex == OscActionCodes::SetCursorColor && colors.size() > colorIndex)
-            {
-                const auto color = til::at(colors, colorIndex);
-                if (color != INVALID_COLOR)
+                else if (color != INVALID_COLOR)
                 {
-                    success = success && _dispatch->SetCursorColor(color);
+                    success = success && _dispatch->SetXtermColorResource(resource, color);
                 }
-                commandIndex++;
-                colorIndex++;
+                resource++;
             }
         }
         break;
@@ -851,7 +838,8 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
     }
     case OscActionCodes::ResetCursorColor:
     {
-        success = _dispatch->SetCursorColor(INVALID_COLOR);
+        /* The reset codes for xterm dynamic resources are the set codes + 100 */
+        success = _dispatch->SetXtermColorResource(parameter - 100u, INVALID_COLOR);
         break;
     }
     case OscActionCodes::Hyperlink:
@@ -938,6 +926,8 @@ bool OutputStateMachineEngine::_GetOscSetColorTable(const std::wstring_view stri
                                                     std::vector<size_t>& tableIndexes,
                                                     std::vector<DWORD>& rgbs) const
 {
+    using namespace std::string_view_literals;
+
     const auto parts = Utils::SplitString(string, L';');
     if (parts.size() < 2)
     {
@@ -949,13 +939,23 @@ bool OutputStateMachineEngine::_GetOscSetColorTable(const std::wstring_view stri
 
     for (size_t i = 0, j = 1; j < parts.size(); i += 2, j += 2)
     {
+        auto&& index = til::at(parts, i);
+        auto&& color = til::at(parts, j);
         unsigned int tableIndex = 0;
-        const auto indexSuccess = Utils::StringToUint(til::at(parts, i), tableIndex);
-        const auto colorOptional = Utils::ColorFromXTermColor(til::at(parts, j));
-        if (indexSuccess && colorOptional.has_value())
+        const auto indexSuccess = Utils::StringToUint(index, tableIndex);
+
+        if (indexSuccess)
         {
-            newTableIndexes.push_back(tableIndex);
-            newRgbs.push_back(colorOptional.value());
+            if (color == L"?"sv) [[unlikely]]
+            {
+                newTableIndexes.push_back(tableIndex);
+                newRgbs.push_back(COLOR_INQUIRY_COLOR);
+            }
+            else if (const auto colorOptional = Utils::ColorFromXTermColor(color))
+            {
+                newTableIndexes.push_back(tableIndex);
+                newRgbs.push_back(colorOptional.value());
+            }
         }
     }
 
@@ -1032,6 +1032,8 @@ bool OutputStateMachineEngine::_ParseHyperlink(const std::wstring_view string,
 bool OutputStateMachineEngine::_GetOscSetColor(const std::wstring_view string,
                                                std::vector<DWORD>& rgbs) const
 {
+    using namespace std::string_view_literals;
+
     const auto parts = Utils::SplitString(string, L';');
     if (parts.size() < 1)
     {
@@ -1039,10 +1041,14 @@ bool OutputStateMachineEngine::_GetOscSetColor(const std::wstring_view string,
     }
 
     std::vector<DWORD> newRgbs;
-    for (size_t i = 0; i < parts.size(); i++)
+    for (auto&& part : parts)
     {
-        const auto colorOptional = Utils::ColorFromXTermColor(til::at(parts, i));
-        if (colorOptional.has_value())
+        if (part == L"?"sv) [[unlikely]]
+        {
+            newRgbs.push_back(COLOR_INQUIRY_COLOR);
+            continue;
+        }
+        else if (const auto colorOptional = Utils::ColorFromXTermColor(part))
         {
             newRgbs.push_back(colorOptional.value());
         }
diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp
index f19ad423fbc..8a5015e25cb 100644
--- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp
+++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp
@@ -1158,12 +1158,8 @@ class StatefulDispatch final : public TermDispatch
         _numTabs{ 0 },
         _tabClear{ false },
         _tabClearTypes{},
-        _setDefaultForeground(false),
-        _defaultForegroundColor{ RGB(0, 0, 0) },
-        _setDefaultBackground(false),
-        _defaultBackgroundColor{ RGB(0, 0, 0) },
-        _setDefaultCursorColor(false),
-        _defaultCursorColor{ RGB(0, 0, 0) },
+        _xtermResourcesChanged{},
+        _xtermResourceValues{},
         _hyperlinkMode{ false },
         _options{ s_cMaxOptions, static_cast<DispatchTypes::GraphicsOptions>(s_uiGraphicsCleared) }, // fill with cleared option
         _colorTable{},
@@ -1432,24 +1428,22 @@ class StatefulDispatch final : public TermDispatch
         return true;
     }
 
-    bool SetDefaultForeground(const DWORD color) noexcept override
+    bool RequestColorTableEntry(const size_t tableIndex) noexcept override
     {
-        _setDefaultForeground = true;
-        _defaultForegroundColor = color;
+        _colorTableEntriesRequested.push_back(tableIndex);
         return true;
     }
 
-    bool SetDefaultBackground(const DWORD color) noexcept override
+    bool SetXtermColorResource(const size_t resource, const DWORD color) override
     {
-        _setDefaultBackground = true;
-        _defaultBackgroundColor = color;
+        _xtermResourcesChanged.push_back(resource);
+        _xtermResourceValues.push_back(color);
         return true;
     }
 
-    bool SetCursorColor(const DWORD color) noexcept override
+    bool RequestXtermColorResource(const size_t resource) override
     {
-        _setDefaultCursorColor = true;
-        _defaultCursorColor = color;
+        _xtermResourcesRequested.push_back(resource);
         return true;
     }
 
@@ -1529,13 +1523,11 @@ class StatefulDispatch final : public TermDispatch
     size_t _numTabs;
     bool _tabClear;
     std::vector<DispatchTypes::TabClearType> _tabClearTypes;
-    bool _setDefaultForeground;
-    DWORD _defaultForegroundColor;
-    bool _setDefaultBackground;
-    DWORD _defaultBackgroundColor;
-    bool _setDefaultCursorColor;
-    DWORD _defaultCursorColor;
+    std::vector<size_t> _xtermResourcesChanged;
+    std::vector<DWORD> _xtermResourceValues;
+    std::vector<size_t> _xtermResourcesRequested;
     bool _setColorTableEntry;
+    std::vector<size_t> _colorTableEntriesRequested;
     bool _hyperlinkMode;
     std::wstring _copyContent;
     std::wstring _uri;
@@ -2822,105 +2814,114 @@ class StateMachineExternalTest final
 
         // Single param
         mach.ProcessString(L"\033]10;rgb:1/1/1\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultForegroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;rgb:12/34/56\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_defaultForegroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#111\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#123456\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_defaultForegroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;DarkOrange\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultForegroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         // Multiple params
         mach.ProcessString(L"\033]10;#111;rgb:2/2/2\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(2u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[1]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_xtermResourceValues[1]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#111;DarkOrange\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(2u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[1]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_xtermResourceValues[1]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#111;DarkOrange;rgb:2/2/2\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultBackgroundColor);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultCursorColor);
-        VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_defaultCursorColor);
+        VERIFY_ARE_EQUAL(3u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[1]);
+        VERIFY_ARE_EQUAL(12u, pDispatch->_xtermResourcesChanged[2]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_xtermResourceValues[1]);
+        VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_xtermResourceValues[2]);
 
         pDispatch->ClearState();
 
         // Partially valid multi-param sequences.
         mach.ProcessString(L"\033]10;#111;\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#111;rgb:\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
-        VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#111;#2\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
-        VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;;rgb:1/1/1\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#1;rgb:1/1/1\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         // Invalid sequences.
         mach.ProcessString(L"\033]10;rgb:1/1/\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]10;#1\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
 
         pDispatch->ClearState();
     }
@@ -2933,100 +2934,115 @@ class StateMachineExternalTest final
         StateMachine mach(std::move(engine));
 
         mach.ProcessString(L"\033]11;rgb:1/1/1\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         // Single param
         mach.ProcessString(L"\033]11;rgb:12/34/56\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#111\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#123456\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;DarkOrange\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         // Multiple params
         mach.ProcessString(L"\033]11;#111;rgb:2/2/2\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
-        VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_defaultCursorColor);
+        VERIFY_ARE_EQUAL(2u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(12u, pDispatch->_xtermResourcesChanged[1]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_xtermResourceValues[1]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#111;DarkOrange\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
-        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultCursorColor);
+        VERIFY_ARE_EQUAL(2u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(12u, pDispatch->_xtermResourcesChanged[1]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_xtermResourceValues[1]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#111;DarkOrange;rgb:2/2/2\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
-        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultCursorColor);
-        // The third param is out of range.
+        VERIFY_ARE_EQUAL(3u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(12u, pDispatch->_xtermResourcesChanged[1]);
+        VERIFY_ARE_EQUAL(13u, pDispatch->_xtermResourcesChanged[2]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_xtermResourceValues[1]);
+        // The third param is out of range, but it's technically still in compliance to set it
 
         pDispatch->ClearState();
 
         // Partially valid multi-param sequences.
         mach.ProcessString(L"\033]11;#111;\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#111;rgb:\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#111;#2\033\\");
-        VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
-        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;;rgb:1/1/1\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultCursorColor);
-        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultCursorColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(12u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#1;rgb:1/1/1\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
-        VERIFY_IS_TRUE(pDispatch->_setDefaultCursorColor);
-        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultCursorColor);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(12u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_xtermResourceValues[0]);
 
         pDispatch->ClearState();
 
         // Invalid sequences.
         mach.ProcessString(L"\033]11;rgb:1/1/\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
 
         pDispatch->ClearState();
 
         mach.ProcessString(L"\033]11;#1\033\\");
-        VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
 
         pDispatch->ClearState();
     }
@@ -3178,6 +3194,88 @@ class StateMachineExternalTest final
         pDispatch->ClearState();
     }
 
+    TEST_METHOD(TestOscGetColorTableEntry)
+    {
+        auto dispatch = std::make_unique<StatefulDispatch>();
+        auto pDispatch = dispatch.get();
+        auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
+        StateMachine mach(std::move(engine));
+
+        mach.ProcessString(L"\033]4;0;?;1;?;2;;3;?;4;?\033\\"); // Inquire about 0-4 skipping 2
+        VERIFY_ARE_EQUAL((std::vector<size_t>{ 0u, 1u, 3u, 4u }), pDispatch->_colorTableEntriesRequested);
+        pDispatch->ClearState();
+
+        mach.ProcessString(L"\033]4;0;rgb:00/00/00;1;rgb:00/00/01;2;?;3;rgb:00/00/03;4;rgb:00/00/04\033\\"); // Set 0-4, except 2 (inquire)
+        VERIFY_ARE_EQUAL(RGB(0, 0, 0), pDispatch->_colorTable.at(0));
+        VERIFY_ARE_EQUAL(RGB(0, 0, 1), pDispatch->_colorTable.at(1));
+        VERIFY_ARE_EQUAL(RGB(0, 0, 3), pDispatch->_colorTable.at(3));
+        VERIFY_ARE_EQUAL(RGB(0, 0, 4), pDispatch->_colorTable.at(4));
+        VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesRequested.size()); // note: (1u, 2u) constructs a 1-length vector containing 2
+        VERIFY_ARE_EQUAL(2u, pDispatch->_colorTableEntriesRequested[0]);
+        pDispatch->ClearState();
+
+        pDispatch->_colorTable.at(3) = RGB(0xfe, 0xed, 0xfa);
+        mach.ProcessString(L"\033]4;0;rgb:f0/00/00;1;?;3\033\\"); // Set 0, inquire 1, truncated 3
+        VERIFY_ARE_EQUAL(RGB(0xf0, 0, 0), pDispatch->_colorTable.at(0));
+        VERIFY_ARE_EQUAL(RGB(0xfe, 0xed, 0xfa), pDispatch->_colorTable.at(3)); // unchanged from before ProcessString
+        VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesRequested.size()); // note: (1u, 2u) constructs a 1-length vector containing 2
+        VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesRequested[0]);
+        pDispatch->ClearState();
+    }
+
+    TEST_METHOD(TestOscXtermResourceReport)
+    {
+        auto dispatch = std::make_unique<StatefulDispatch>();
+        auto pDispatch = dispatch.get();
+        auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
+        StateMachine mach(std::move(engine));
+
+        mach.ProcessString(L"\033]10;?\033\\");
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesRequested.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesRequested[0]);
+        pDispatch->ClearState();
+
+        // Two params, skip first
+        mach.ProcessString(L"\033]10;;?\033\\");
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesRequested.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesRequested[0]);
+        pDispatch->ClearState();
+
+        // Two params, set first
+        mach.ProcessString(L"\033]10;rgb:11/22/33;?\033\\");
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x22, 0x33), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesRequested.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesRequested[0]);
+        pDispatch->ClearState();
+
+        // Two params, set first, starting at 12
+        mach.ProcessString(L"\033]12;rgb:11/22/33;?\033\\");
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(12u, pDispatch->_xtermResourcesChanged[0]);
+        VERIFY_ARE_EQUAL(RGB(0x11, 0x22, 0x33), pDispatch->_xtermResourceValues[0]);
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesRequested.size());
+        VERIFY_ARE_EQUAL(13u, pDispatch->_xtermResourcesRequested[0]);
+        pDispatch->ClearState();
+
+        // Request all 10
+        mach.ProcessString(L"\033]10;?;?;?;?;?;?;?;?;?;?\033\\");
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
+        std::vector<size_t> expected{ 10u, 11u, 12u, 13u, 14u, 15u, 16u, 17u, 18u, 19u };
+        VERIFY_ARE_EQUAL(expected, pDispatch->_xtermResourcesRequested);
+        pDispatch->ClearState();
+
+        // Request out of range
+        mach.ProcessString(L"\033]10;;?\033\\");
+        VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
+        VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesRequested.size());
+        VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesRequested[0]);
+        pDispatch->ClearState();
+    }
+
     TEST_METHOD(TestOscSetWindowTitle)
     {
         BEGIN_TEST_METHOD_PROPERTIES()


Test patch: