installed pty
[VSoRC/.git] / node_modules / node-pty / deps / winpty / src / agent / DebugShowInput.cc
1 // Copyright (c) 2015 Ryan Prichard
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to
5 // deal in the Software without restriction, including without limitation the
6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 // sell copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 // IN THE SOFTWARE.
20
21 #include "DebugShowInput.h"
22
23 #include <windows.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include <string>
29
30 #include "../shared/StringBuilder.h"
31 #include "InputMap.h"
32
33 namespace {
34
35 struct Flag {
36     DWORD value;
37     const char *text;
38 };
39
40 static const Flag kButtonStates[] = {
41     { FROM_LEFT_1ST_BUTTON_PRESSED, "1" },
42     { FROM_LEFT_2ND_BUTTON_PRESSED, "2" },
43     { FROM_LEFT_3RD_BUTTON_PRESSED, "3" },
44     { FROM_LEFT_4TH_BUTTON_PRESSED, "4" },
45     { RIGHTMOST_BUTTON_PRESSED,     "R" },
46 };
47
48 static const Flag kControlKeyStates[] = {
49     { CAPSLOCK_ON,          "CapsLock"      },
50     { ENHANCED_KEY,         "Enhanced"      },
51     { LEFT_ALT_PRESSED,     "LAlt"          },
52     { LEFT_CTRL_PRESSED,    "LCtrl"         },
53     { NUMLOCK_ON,           "NumLock"       },
54     { RIGHT_ALT_PRESSED,    "RAlt"          },
55     { RIGHT_CTRL_PRESSED,   "RCtrl"         },
56     { SCROLLLOCK_ON,        "ScrollLock"    },
57     { SHIFT_PRESSED,        "Shift"         },
58 };
59
60 static const Flag kMouseEventFlags[] = {
61     { DOUBLE_CLICK,         "Double"        },
62     { 8/*MOUSE_HWHEELED*/,  "HWheel"        },
63     { MOUSE_MOVED,          "Move"          },
64     { MOUSE_WHEELED,        "Wheel"         },
65 };
66
67 static void writeFlags(StringBuilder &out, DWORD flags,
68                        const char *remainderName,
69                        const Flag *table, size_t tableSize,
70                        char pre, char sep, char post) {
71     DWORD remaining = flags;
72     bool wroteSomething = false;
73     for (size_t i = 0; i < tableSize; ++i) {
74         const Flag &f = table[i];
75         if ((f.value & flags) == f.value) {
76             if (!wroteSomething && pre != '\0') {
77                 out << pre;
78             } else if (wroteSomething && sep != '\0') {
79                 out << sep;
80             }
81             out << f.text;
82             wroteSomething = true;
83             remaining &= ~f.value;
84         }
85     }
86     if (remaining != 0) {
87         if (!wroteSomething && pre != '\0') {
88             out << pre;
89         } else if (wroteSomething && sep != '\0') {
90             out << sep;
91         }
92         out << remainderName << "(0x" << hexOfInt(remaining) << ')';
93         wroteSomething = true;
94     }
95     if (wroteSomething && post != '\0') {
96         out << post;
97     }
98 }
99
100 template <size_t n>
101 static void writeFlags(StringBuilder &out, DWORD flags,
102                        const char *remainderName,
103                        const Flag (&table)[n],
104                        char pre, char sep, char post) {
105     writeFlags(out, flags, remainderName, table, n, pre, sep, post);
106 }
107
108 } // anonymous namespace
109
110 std::string controlKeyStatePrefix(DWORD controlKeyState) {
111     StringBuilder sb;
112     writeFlags(sb, controlKeyState,
113                "keyState", kControlKeyStates, '\0', '-', '-');
114     return sb.str_moved();
115 }
116
117 std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer) {
118     const uint16_t buttons = mer.dwButtonState & 0xFFFF;
119     const int16_t wheel = mer.dwButtonState >> 16;
120     StringBuilder sb;
121     sb << "pos=" << mer.dwMousePosition.X << ','
122                  << mer.dwMousePosition.Y;
123     writeFlags(sb, mer.dwControlKeyState, "keyState", kControlKeyStates, ' ', ' ', '\0');
124     writeFlags(sb, mer.dwEventFlags, "flags", kMouseEventFlags, ' ', ' ', '\0');
125     writeFlags(sb, buttons, "buttons", kButtonStates, ' ', ' ', '\0');
126     if (wheel != 0) {
127         sb << " wheel=" << wheel;
128     }
129     return sb.str_moved();
130 }
131
132 void debugShowInput(bool enableMouse, bool escapeInput) {
133     HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
134     DWORD origConsoleMode = 0;
135     if (!GetConsoleMode(conin, &origConsoleMode)) {
136         fprintf(stderr, "Error: could not read console mode -- "
137                         "is STDIN a console handle?\n");
138         exit(1);
139     }
140     DWORD restoreConsoleMode = origConsoleMode;
141     if (enableMouse && !(restoreConsoleMode & ENABLE_EXTENDED_FLAGS)) {
142         // We need to disable QuickEdit mode, because it blocks mouse events.
143         // If ENABLE_EXTENDED_FLAGS wasn't originally in the console mode, then
144         // we have no way of knowning whether QuickEdit or InsertMode are
145         // currently enabled.  Enable them both (eventually), because they're
146         // sensible defaults.  This case shouldn't happen typically.  See
147         // misc/EnableExtendedFlags.txt.
148         restoreConsoleMode |= ENABLE_EXTENDED_FLAGS;
149         restoreConsoleMode |= ENABLE_QUICK_EDIT_MODE;
150         restoreConsoleMode |= ENABLE_INSERT_MODE;
151     }
152     DWORD newConsoleMode = restoreConsoleMode;
153     newConsoleMode &= ~ENABLE_PROCESSED_INPUT;
154     newConsoleMode &= ~ENABLE_LINE_INPUT;
155     newConsoleMode &= ~ENABLE_ECHO_INPUT;
156     newConsoleMode |= ENABLE_WINDOW_INPUT;
157     if (enableMouse) {
158         newConsoleMode |= ENABLE_MOUSE_INPUT;
159         newConsoleMode &= ~ENABLE_QUICK_EDIT_MODE;
160     } else {
161         newConsoleMode &= ~ENABLE_MOUSE_INPUT;
162     }
163     if (escapeInput) {
164         // As of this writing (2016-06-05), Microsoft has shipped two preview
165         // builds of Windows 10 (14316 and 14342) that include a new "Windows
166         // Subsystem for Linux" that runs Ubuntu in a new subsystem.  Running
167         // bash in this subsystem requires the non-legacy console mode, and the
168         // console input buffer is put into a special mode where escape
169         // sequences are written into the console input buffer.  This mode is
170         // enabled with the 0x200 flag, which is as-yet undocumented.
171         // See https://github.com/rprichard/winpty/issues/82.
172         newConsoleMode |= 0x200;
173     }
174     if (!SetConsoleMode(conin, newConsoleMode)) {
175         fprintf(stderr, "Error: could not set console mode "
176             "(0x%x -> 0x%x -> 0x%x)\n",
177             static_cast<unsigned int>(origConsoleMode),
178             static_cast<unsigned int>(newConsoleMode),
179             static_cast<unsigned int>(restoreConsoleMode));
180         exit(1);
181     }
182     printf("\nPress any keys -- Ctrl-D exits\n\n");
183     INPUT_RECORD records[32];
184     DWORD actual = 0;
185     bool finished = false;
186     while (!finished &&
187             ReadConsoleInputW(conin, records, 32, &actual) && actual >= 1) {
188         StringBuilder sb;
189         for (DWORD i = 0; i < actual; ++i) {
190             const INPUT_RECORD &record = records[i];
191             if (record.EventType == KEY_EVENT) {
192                 const KEY_EVENT_RECORD &ker = record.Event.KeyEvent;
193                 InputMap::Key key = {
194                     ker.wVirtualKeyCode,
195                     ker.uChar.UnicodeChar,
196                     static_cast<uint16_t>(ker.dwControlKeyState),
197                 };
198                 sb << "key: " << (ker.bKeyDown ? "dn" : "up")
199                    << " rpt=" << ker.wRepeatCount
200                    << " scn=" << (ker.wVirtualScanCode ? "0x" : "") << hexOfInt(ker.wVirtualScanCode)
201                    << ' ' << key.toString() << '\n';
202                 if ((ker.dwControlKeyState &
203                         (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
204                         ker.wVirtualKeyCode == 'D') {
205                     finished = true;
206                     break;
207                 } else if (ker.wVirtualKeyCode == 0 &&
208                         ker.wVirtualScanCode == 0 &&
209                         ker.uChar.UnicodeChar == 4) {
210                     // Also look for a zeroed-out Ctrl-D record generated for
211                     // ENABLE_VIRTUAL_TERMINAL_INPUT.
212                     finished = true;
213                     break;
214                 }
215             } else if (record.EventType == MOUSE_EVENT) {
216                 const MOUSE_EVENT_RECORD &mer = record.Event.MouseEvent;
217                 sb << "mouse: " << mouseEventToString(mer) << '\n';
218             } else if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
219                 const WINDOW_BUFFER_SIZE_RECORD &wbsr =
220                     record.Event.WindowBufferSizeEvent;
221                 sb << "buffer-resized: dwSize=("
222                    << wbsr.dwSize.X << ','
223                    << wbsr.dwSize.Y << ")\n";
224             } else if (record.EventType == MENU_EVENT) {
225                 const MENU_EVENT_RECORD &mer = record.Event.MenuEvent;
226                 sb << "menu-event: commandId=0x"
227                    << hexOfInt(mer.dwCommandId) << '\n';
228             } else if (record.EventType == FOCUS_EVENT) {
229                 const FOCUS_EVENT_RECORD &fer = record.Event.FocusEvent;
230                 sb << "focus: " << (fer.bSetFocus ? "gained" : "lost") << '\n';
231             }
232         }
233
234         const auto str = sb.str_moved();
235         fwrite(str.data(), 1, str.size(), stdout);
236         fflush(stdout);
237     }
238     SetConsoleMode(conin, restoreConsoleMode);
239 }