master
cpp 202 lines 5.28 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 ConsoleState.cpp
8
9 Abstract:
10
11 This file contains function definitions for the ConsoleState helper class.
12
13 --*/
14
15 #include "precomp.h"
16 #include "svccomm.hpp"
17 #include "ConsoleState.h"
18 #pragma hdrstop
19
20 namespace {
21
22 void ChangeConsoleMode(_In_ HANDLE Handle, _In_ DWORD Mode)
23 {
24 // Use the invalid parameter error code to detect the v1 console that does not support the provided mode.
25 // This can be improved in the future when a more elegant solution exists.
26 //
27 // N.B. Ignore failures setting the mode if the console has already disconnected.
28 if (!SetConsoleMode(Handle, Mode))
29 {
30 // DISABLE_NEWLINE_AUTO_RETURN is not supported everywhere, if the flag was present fall back and try again.
31 if (WI_IsFlagSet(Mode, DISABLE_NEWLINE_AUTO_RETURN))
32 {
33 Mode = WI_ClearFlag(Mode, DISABLE_NEWLINE_AUTO_RETURN);
34 if (SetConsoleMode(Handle, Mode))
35 {
36 return;
37 }
38 }
39
40 switch (GetLastError())
41 {
42 case ERROR_PIPE_NOT_CONNECTED:
43 break;
44
45 case ERROR_INVALID_PARAMETER:
46 THROW_HR_MSG(WSL_E_CONSOLE, "SetConsoleMode(0x%x) failed", Mode);
47
48 default:
49 THROW_LAST_ERROR_MSG("SetConsoleMode(0x%x) failed", Mode);
50 }
51 }
52 }
53
54 void TrySetConsoleMode(_In_ HANDLE Handle, _In_ DWORD Mode)
55 try
56 {
57 ChangeConsoleMode(Handle, Mode);
58 }
59 CATCH_LOG()
60
61 } // namespace
62
63 namespace wsl::windows::common {
64
65 ConsoleState::ConsoleState()
66 {
67 m_InputHandle.reset(
68 CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));
69
70 if (!m_InputHandle)
71 {
72 LOG_LAST_ERROR_MSG("CreateFileW(CONIN$) failed");
73 }
74
75 m_OutputHandle.reset(
76 CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));
77
78 if (!m_OutputHandle)
79 {
80 LOG_LAST_ERROR_MSG("CreateFileW(CONOUT$) failed");
81 }
82 }
83
84 void ConsoleState::SetInteractiveMode()
85 {
86 if (m_interactiveModeConfigured)
87 {
88 return;
89 }
90
91 // Ensure console state is restored if this method throws.
92 auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { RestoreConsoleState(); });
93
94 if (m_InputHandle)
95 {
96 m_SavedInputCodePage = GetConsoleCP();
97 LOG_IF_WIN32_BOOL_FALSE(SetConsoleCP(CP_UTF8));
98
99 // Configure for raw input with VT support.
100 DWORD mode;
101 THROW_LAST_ERROR_IF(!GetConsoleMode(m_InputHandle.get(), &mode));
102
103 DWORD NewMode = mode;
104 WI_SetAllFlags(NewMode, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT);
105 WI_ClearAllFlags(NewMode, ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
106 ChangeConsoleMode(m_InputHandle.get(), NewMode);
107 m_SavedInputMode = mode;
108 }
109
110 if (m_OutputHandle)
111 {
112 if (!m_SavedOutputCodePage.has_value())
113 {
114 m_SavedOutputCodePage = GetConsoleOutputCP();
115 }
116
117 LOG_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(CP_UTF8));
118
119 // Configure for VT output.
120 DWORD mode;
121 THROW_LAST_ERROR_IF(!GetConsoleMode(m_OutputHandle.get(), &mode));
122
123 DWORD NewMode = mode;
124 WI_SetAllFlags(NewMode, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);
125 ChangeConsoleMode(m_OutputHandle.get(), NewMode);
126 m_SavedOutputMode = mode;
127 }
128
129 m_interactiveModeConfigured = true;
130 cleanup.release();
131 }
132
133 void ConsoleState::SetOutputCodePageUtf8()
134 {
135 if (!m_OutputHandle)
136 {
137 return;
138 }
139
140 if (!m_SavedOutputCodePage.has_value())
141 {
142 m_SavedOutputCodePage = GetConsoleOutputCP();
143 }
144
145 LOG_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(CP_UTF8));
146 }
147
148 ConsoleState::~ConsoleState()
149 {
150 RestoreConsoleState();
151 }
152
153 void ConsoleState::RestoreConsoleState()
154 {
155 if (m_InputHandle)
156 {
157 if (m_SavedInputCodePage.has_value())
158 {
159 LOG_IF_WIN32_BOOL_FALSE(SetConsoleCP(m_SavedInputCodePage.value()));
160 m_SavedInputCodePage.reset();
161 }
162
163 if (m_SavedInputMode.has_value())
164 {
165 TrySetConsoleMode(m_InputHandle.get(), m_SavedInputMode.value());
166 m_SavedInputMode.reset();
167 }
168 }
169
170 if (m_OutputHandle)
171 {
172 if (m_SavedOutputCodePage.has_value())
173 {
174 LOG_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(m_SavedOutputCodePage.value()));
175 m_SavedOutputCodePage.reset();
176 }
177
178 if (m_SavedOutputMode.has_value())
179 {
180 TrySetConsoleMode(m_OutputHandle.get(), m_SavedOutputMode.value());
181 m_SavedOutputMode.reset();
182 }
183 }
184 }
185
186 COORD ConsoleState::GetWindowSize() const
187 {
188 if (m_OutputHandle)
189 {
190 CONSOLE_SCREEN_BUFFER_INFOEX Info{};
191 Info.cbSize = sizeof(Info);
192 THROW_IF_WIN32_BOOL_FALSE(GetConsoleScreenBufferInfoEx(m_OutputHandle.get(), &Info));
193 return {
194 static_cast<short>(Info.srWindow.Right - Info.srWindow.Left + 1),
195 static_cast<short>(Info.srWindow.Bottom - Info.srWindow.Top + 1)};
196 }
197
198 LOG_HR_MSG(E_UNEXPECTED, "No console handle available for GetWindowSize");
199 return {80, 24};
200 }
201
202 } // namespace wsl::windows::common