Repository: microsoft/terminal (language: cpp)

Issue:
Mouse events are double-encoded with win32-input-mode
### Windows Terminal version

1.23.2421.0

### Windows build number

_No response_

### Other Software

_No response_

### Steps to reproduce

```cpp
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

int main() {
    const auto inputHandle = GetStdHandle(STD_INPUT_HANDLE);
    const auto outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    const auto previousInputCP = GetConsoleCP();
    const auto previousOutputCP = GetConsoleOutputCP();
    DWORD previousInputMode = 0;
    DWORD previousOutputMode = 0;
    GetConsoleMode(inputHandle, &previousInputMode);
    GetConsoleMode(outputHandle, &previousOutputMode);

    SetConsoleCP(CP_UTF8);
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleMode(inputHandle, ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_VIRTUAL_TERMINAL_INPUT);
    SetConsoleMode(outputHandle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);

    static bool exit;
    SetConsoleCtrlHandler([](DWORD) -> BOOL {
        exit = true;
        return TRUE;
    }, TRUE);

    WriteFile(outputHandle, "\033[?9001h\x1b[?1003;1006h", 21, nullptr, nullptr);

    char buffer[4096];
    while (!exit) {
        DWORD read;
        ReadFile(inputHandle, &buffer[0], sizeof(buffer), &read, nullptr);
        if (read == 0 || (read == 1 && (buffer[0] == 'q' || buffer[0] == 'Q'))) {
            break;
        }

        // Expand buffer and replace Esc with "\\x1b"
        char expanded[4 * ARRAYSIZE(buffer)];
        size_t expendedLength = 0;
        for (size_t i = 0; i < read; i++) {
            if (buffer[i] == 27) {
                expanded[expendedLength++] = '\\';
                expanded[expendedLength++] = 'x';
                expanded[expendedLength++] = '1';
                expanded[expendedLength++] = 'b';
            } else {
                expanded[expendedLength++] = buffer[i];
            }
        }

        expanded[expendedLength++] = '\r';
        expanded[expendedLength++] = '\n';
        WriteFile(outputHandle, expanded, expendedLength, nullptr, nullptr);
    }

    WriteFile(outputHandle, "\033[?9001l\x1b[?1003;1006l", 21, nullptr, nullptr);
    
    SetConsoleMode(inputHandle, previousInputMode);
    SetConsoleMode(outputHandle, previousOutputMode);
    SetConsoleCP(previousInputCP);
    SetConsoleOutputCP(previousOutputCP);
    return 0;
}
```

### Expected Behavior

_No response_

### Actual Behavior

Double-encoding!


Discussion / hints:

Fix patch:
diff --git a/src/terminal/adapter/IInteractDispatch.hpp b/src/terminal/adapter/IInteractDispatch.hpp
index 4006c4c82a4..b8aba0fb1a4 100644
--- a/src/terminal/adapter/IInteractDispatch.hpp
+++ b/src/terminal/adapter/IInteractDispatch.hpp
@@ -33,6 +33,7 @@ namespace Microsoft::Console::VirtualTerminal
         virtual void WriteInput(const std::span<const INPUT_RECORD>& inputEvents) = 0;
         virtual void WriteCtrlKey(const INPUT_RECORD& event) = 0;
         virtual void WriteString(std::wstring_view string) = 0;
+        virtual void WriteStringRaw(std::wstring_view string) = 0;
         virtual void WindowManipulation(DispatchTypes::WindowManipulationType function, VTParameter parameter1, VTParameter parameter2) = 0;
         virtual void MoveCursor(VTInt row, VTInt col) = 0;
         virtual void FocusChanged(bool focused) = 0;
diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp
index 1f859c6916d..fa1a0826047 100644
--- a/src/terminal/adapter/InteractDispatch.cpp
+++ b/src/terminal/adapter/InteractDispatch.cpp
@@ -53,10 +53,11 @@ void InteractDispatch::WriteCtrlKey(const INPUT_RECORD& event)
     HandleGenericKeyEvent(event, false);
 }
 
-// Method Description:
-// - Writes a string of input to the host.
-// Arguments:
-// - string : a string to write to the console.
+// Call this method to write some plain text to the InputBuffer.
+//
+// Since the hosting terminal for ConPTY may not support win32-input-mode,
+// it may send an "A" key as an "A", for which we need to generate up/down events.
+// Because of this, we cannot simply call InputBuffer::WriteString directly.
 void InteractDispatch::WriteString(const std::wstring_view string)
 {
     if (!string.empty())
@@ -73,6 +74,12 @@ void InteractDispatch::WriteString(const std::wstring_view string)
     }
 }
 
+void InteractDispatch::WriteStringRaw(std::wstring_view string)
+{
+    const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
+    gci.GetActiveInputBuffer()->WriteString(string);
+}
+
 //Method Description:
 // Window Manipulation - Performs a variety of actions relating to the window,
 //      such as moving the window position, resizing the window, querying
diff --git a/src/terminal/adapter/InteractDispatch.hpp b/src/terminal/adapter/InteractDispatch.hpp
index 189d73e2a52..2fe63b08fd1 100644
--- a/src/terminal/adapter/InteractDispatch.hpp
+++ b/src/terminal/adapter/InteractDispatch.hpp
@@ -30,6 +30,7 @@ namespace Microsoft::Console::VirtualTerminal
         void WriteInput(const std::span<const INPUT_RECORD>& inputEvents) override;
         void WriteCtrlKey(const INPUT_RECORD& event) override;
         void WriteString(std::wstring_view string) override;
+        void WriteStringRaw(std::wstring_view string) override;
         void WindowManipulation(DispatchTypes::WindowManipulationType function, VTParameter parameter1, VTParameter parameter2) override;
         void MoveCursor(VTInt row, VTInt col) override;
         void FocusChanged(bool focused) override;
diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp
index 70e3991ee49..9bf02564a83 100644
--- a/src/terminal/parser/InputStateMachineEngine.cpp
+++ b/src/terminal/parser/InputStateMachineEngine.cpp
@@ -293,27 +293,10 @@ bool InputStateMachineEngine::ActionPrintString(const std::wstring_view string)
 // - true iff we successfully dispatched the sequence.
 bool InputStateMachineEngine::ActionPassThroughString(const std::wstring_view string)
 {
-    if (string.empty())
-    {
-        return true;
-    }
-
-    if (_pDispatch->IsVtInputEnabled())
-    {
-        // Synthesize string into key events that we'll write to the buffer
-        // similar to TerminalInput::_SendInputSequence
-        InputEventQueue inputEvents;
-        for (const auto& wch : string)
-        {
-            inputEvents.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0));
-        }
-        _pDispatch->WriteInput(inputEvents);
-    }
-    else
+    if (!string.empty())
     {
-        _pDispatch->WriteString(string);
+        _pDispatch->WriteStringRaw(string);
     }
-
     return true;
 }
 
diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp
index 00684b36ff0..cdfaf1ac1e6 100644
--- a/src/terminal/parser/ut_parser/InputEngineTest.cpp
+++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp
@@ -322,6 +322,7 @@ class Microsoft::Console::VirtualTerminal::TestInteractDispatch final : public I
                                     const VTParameter parameter1,
                                     const VTParameter parameter2) override; // DTTERM_WindowManipulation
     virtual void WriteString(const std::wstring_view string) override;
+    virtual void WriteStringRaw(const std::wstring_view string) override;
 
     virtual void MoveCursor(const VTInt row,
                             const VTInt col) override;
@@ -377,6 +378,18 @@ void TestInteractDispatch::WriteString(const std::wstring_view string)
     WriteInput(keyEvents);
 }
 
+void TestInteractDispatch::WriteStringRaw(const std::wstring_view string)
+{
+    InputEventQueue keyEvents;
+
+    for (const auto& wch : string)
+    {
+        keyEvents.push_back(SynthesizeKeyEvent(true, 1, 0, 0, wch, 0));
+    }
+
+    WriteInput(keyEvents);
+}
+
 void TestInteractDispatch::MoveCursor(const VTInt row, const VTInt col)
 {
     VERIFY_IS_TRUE(_testState->_expectCursorPosition);
diff --git a/src/tools/vtpipeterm/main.cpp b/src/tools/vtpipeterm/main.cpp
index 51be0dc5fda..fa79b0b9cf4 100644
--- a/src/tools/vtpipeterm/main.cpp
+++ b/src/tools/vtpipeterm/main.cpp
@@ -184,7 +184,7 @@ static int run(int argc, const wchar_t* argv[])
             if (write != 0)
             {
                 DWORD written;
-                if (!WriteFile(pipe.server.get(), &inputConptyBuffer[0], write, &written, nullptr) || written != read)
+                if (!WriteFile(pipe.server.get(), &inputConptyBuffer[0], write, &written, nullptr) || written != write)
                 {
                     return 0;
                 }


Test patch: