diff --git a/src/agent/DebugShowInput.cc b/src/agent/DebugShowInput.cc new file mode 100755 index 0000000..f987256 --- /dev/null +++ b/src/agent/DebugShowInput.cc @@ -0,0 +1,203 @@ +// Copyright (c) 2015 Ryan Prichard +// +// 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. + +#include "DebugShowInput.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "InputMap.h" + +namespace { + +struct Flag { + DWORD value; + const char *text; +}; + +static const Flag kButtonStates[] = { + { FROM_LEFT_1ST_BUTTON_PRESSED, "1" }, + { FROM_LEFT_2ND_BUTTON_PRESSED, "2" }, + { FROM_LEFT_3RD_BUTTON_PRESSED, "3" }, + { FROM_LEFT_4TH_BUTTON_PRESSED, "4" }, + { RIGHTMOST_BUTTON_PRESSED, "R" }, +}; + +static const Flag kControlKeyStates[] = { + { CAPSLOCK_ON, "CapsLock" }, + { ENHANCED_KEY, "Enhanced" }, + { LEFT_ALT_PRESSED, "LAlt" }, + { LEFT_CTRL_PRESSED, "LCtrl" }, + { NUMLOCK_ON, "NumLock" }, + { RIGHT_ALT_PRESSED, "RAlt" }, + { RIGHT_CTRL_PRESSED, "RCtrl" }, + { SCROLLLOCK_ON, "ScrollLock" }, + { SHIFT_PRESSED, "Shift" }, +}; + +static const Flag kMouseEventFlags[] = { + { DOUBLE_CLICK, "Double" }, + { 8/*MOUSE_HWHEELED*/, "HWheel" }, + { MOUSE_MOVED, "Move" }, + { MOUSE_WHEELED, "Wheel" }, +}; + +static void writeFlags(std::ostream &out, DWORD flags, + const char *remainderName, + const Flag *table, size_t tableSize, + char pre, char sep, char post) { + DWORD remaining = flags; + bool wroteSomething = false; + for (size_t i = 0; i < tableSize; ++i) { + const Flag &f = table[i]; + if ((f.value & flags) == f.value) { + if (!wroteSomething && pre != '\0') { + out.put(pre); + } else if (wroteSomething && sep != '\0') { + out.put(sep); + } + out << f.text; + wroteSomething = true; + remaining &= ~f.value; + } + } + if (remaining != 0) { + if (!wroteSomething && pre != '\0') { + out.put(pre); + } else if (wroteSomething && sep != '\0') { + out.put(sep); + } + std::ios oldState(NULL); + oldState.copyfmt(out); + out << std::hex << remainderName << "(0x" << remaining << ")"; + out.copyfmt(oldState); + wroteSomething = true; + } + if (wroteSomething && post != '\0') { + out.put(post); + } +} + +template +static void writeFlags(std::ostream &out, DWORD flags, + const char *remainderName, + const Flag (&table)[n], + char pre, char sep, char post) { + writeFlags(out, flags, remainderName, table, n, pre, sep, post); +} + +} // anonymous namespace + +std::string controlKeyStatePrefix(DWORD controlKeyState) { + std::stringstream ss; + writeFlags(ss, controlKeyState, + "keyState", kControlKeyStates, '\0', '-', '-'); + return ss.str(); +} + +std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer) { + const uint16_t buttons = mer.dwButtonState & 0xFFFF; + const int16_t wheel = mer.dwButtonState >> 16; + std::stringstream ss; + ss << std::dec << "pos=" << mer.dwMousePosition.X << ',' + << mer.dwMousePosition.Y; + writeFlags(ss, mer.dwControlKeyState, "keyState", kControlKeyStates, ' ', ' ', '\0'); + writeFlags(ss, mer.dwEventFlags, "flags", kMouseEventFlags, ' ', ' ', '\0'); + writeFlags(ss, buttons, "buttons", kButtonStates, ' ', ' ', '\0'); + if (wheel != 0) { + ss << " wheel=" << std::dec << wheel; + } + return ss.str(); +} + +void debugShowInput(bool enableMouse) { + HANDLE conin = GetStdHandle(STD_INPUT_HANDLE); + DWORD origConsoleMode = 0; + if (!GetConsoleMode(conin, &origConsoleMode)) { + fprintf(stderr, "Error: could not read console mode -- " + "is STDIN a console handle?\n"); + exit(1); + } + DWORD newConsoleMode = origConsoleMode; + newConsoleMode &= ~ENABLE_PROCESSED_INPUT; + newConsoleMode &= ~ENABLE_LINE_INPUT; + newConsoleMode &= ~ENABLE_ECHO_INPUT; + if (enableMouse) { + newConsoleMode |= ENABLE_MOUSE_INPUT; + } else { + newConsoleMode &= ~ENABLE_MOUSE_INPUT; + } + if (!SetConsoleMode(conin, newConsoleMode)) { + fprintf(stderr, "Error: could not set console mode\n"); + exit(1); + } + printf("\nPress any keys -- Ctrl-D exits\n\n"); + INPUT_RECORD records[32]; + DWORD actual = 0; + bool finished = false; + while (!finished && + ReadConsoleInputW(conin, records, 32, &actual) && actual >= 1) { + std::stringstream ss; + for (DWORD i = 0; i < actual; ++i) { + const INPUT_RECORD &record = records[i]; + if (record.EventType == KEY_EVENT) { + const KEY_EVENT_RECORD &ker = record.Event.KeyEvent; + InputMap::Key key = { + ker.wVirtualKeyCode, + ker.uChar.UnicodeChar, + static_cast(ker.dwControlKeyState), + }; + ss << "key: " << (ker.bKeyDown ? "dn" : "up") + << " rpt=" << std::dec << ker.wRepeatCount + << " scn=" << std::dec << ker.wVirtualScanCode + << ' ' << key.toString() << '\n'; + if ((ker.dwControlKeyState & LEFT_CTRL_PRESSED) && + ker.wVirtualKeyCode == 'D') { + finished = true; + break; + } + } else if (record.EventType == MOUSE_EVENT) { + const MOUSE_EVENT_RECORD &mer = record.Event.MouseEvent; + ss << "mouse: " << mouseEventToString(mer).c_str() << '\n'; + } else if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) { + const WINDOW_BUFFER_SIZE_RECORD &wbsr = + record.Event.WindowBufferSizeEvent; + ss << "buffer-resized: dwSize=(" + << std::dec << wbsr.dwSize.X << ',' + << wbsr.dwSize.Y << ")\n"; + } else if (record.EventType == MENU_EVENT) { + const MENU_EVENT_RECORD &mer = record.Event.MenuEvent; + ss << "menu-event: commandId=0x" + << std::hex << mer.dwCommandId << '\n'; + } else if (record.EventType == FOCUS_EVENT) { + const FOCUS_EVENT_RECORD &fer = record.Event.FocusEvent; + ss << "focus: " << (fer.bSetFocus ? "gained" : "lost") << '\n'; + } + } + std::cout << ss.str(); + } + SetConsoleMode(conin, origConsoleMode); +} diff --git a/src/agent/DebugShowInput.h b/src/agent/DebugShowInput.h new file mode 100755 index 0000000..27f286e --- /dev/null +++ b/src/agent/DebugShowInput.h @@ -0,0 +1,32 @@ +// Copyright (c) 2015 Ryan Prichard +// +// 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. + +#ifndef AGENT_DEBUG_SHOW_INPUT_H +#define AGENT_DEBUG_SHOW_INPUT_H + +#include + +#include + +std::string controlKeyStatePrefix(DWORD controlKeyState); +std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer); +void debugShowInput(bool enableMouse); + +#endif // AGENT_DEBUG_SHOW_INPUT_H diff --git a/src/agent/InputMap.cc b/src/agent/InputMap.cc index 643aba8..a9e3da2 100755 --- a/src/agent/InputMap.cc +++ b/src/agent/InputMap.cc @@ -25,6 +25,7 @@ #include #include +#include "DebugShowInput.h" #include "SimplePool.h" #include "../shared/DebugClient.h" #include "../shared/UnixCtrlChars.h" @@ -112,15 +113,7 @@ static const char *getVirtualKeyString(int virtualKey) std::string InputMap::Key::toString() const { std::string ret; - if (keyState & CAPSLOCK_ON) { ret += "CapsLock-"; } - if (keyState & ENHANCED_KEY) { ret += "Enhanced-"; } - if (keyState & LEFT_ALT_PRESSED) { ret += "LAlt-"; } - if (keyState & LEFT_CTRL_PRESSED) { ret += "LCtrl-"; } - if (keyState & NUMLOCK_ON) { ret += "NumLock-"; } - if (keyState & RIGHT_ALT_PRESSED) { ret += "RAlt-"; } - if (keyState & RIGHT_CTRL_PRESSED) { ret += "RCtrl-"; } - if (keyState & SCROLLLOCK_ON) { ret += "ScrollLock-"; } - if (keyState & SHIFT_PRESSED) { ret += "Shift-"; } + ret += controlKeyStatePrefix(keyState); char buf[256]; const char *vkString = getVirtualKeyString(virtualKey); if (vkString != NULL) { diff --git a/src/agent/main.cc b/src/agent/main.cc index 1a378f0..3611de9 100644 --- a/src/agent/main.cc +++ b/src/agent/main.cc @@ -22,7 +22,7 @@ #include #include "Agent.h" -#include "InputMap.h" +#include "DebugShowInput.h" #include "../shared/WinptyAssert.h" #include "../shared/WinptyVersion.h" @@ -36,8 +36,10 @@ const char USAGE[] = "Usage: %s [options]\n" "\n" "Options:\n" -" --showkey Dump KEY_EVENT records read from the console input buffer\n" -" --version Print the winpty version\n"; +" --show-input Dump INPUT_RECORDs from the console input buffer\n" +" --show-input --with-mouse\n" +" Include MOUSE_INPUT_RECORDs in the dump output\n" +" --version Print the winpty version\n"; static wchar_t *heapMbsToWcs(const char *text) { @@ -49,45 +51,6 @@ static wchar_t *heapMbsToWcs(const char *text) return ret; } -static void debugShowKey() -{ - HANDLE conin = GetStdHandle(STD_INPUT_HANDLE); - DWORD consoleMode = 0; - if (!GetConsoleMode(conin, &consoleMode) || - !SetConsoleMode(conin, consoleMode & - ~(ENABLE_PROCESSED_INPUT | - ENABLE_LINE_INPUT | - ENABLE_ECHO_INPUT))) { - printf("Error: could not set console mode -- " - "is STDIN a console handle?\n"); - exit(1); - } - printf("\nPress any keys -- Ctrl-D exits\n\n"); - INPUT_RECORD record; - DWORD actual = 0; - while (ReadConsoleInputW(conin, &record, 1, &actual) && actual == 1) { - if (record.EventType != KEY_EVENT) { - continue; - } - KEY_EVENT_RECORD &ker = record.Event.KeyEvent; - InputMap::Key key = { - ker.wVirtualKeyCode, - ker.uChar.UnicodeChar, - static_cast(ker.dwControlKeyState), - }; - printf("%s rpt=%d scn=%d %s\n", - ker.bKeyDown ? "dn" : "up", - ker.wRepeatCount, - ker.wVirtualScanCode, - key.toString().c_str()); - if ((ker.dwControlKeyState & LEFT_CTRL_PRESSED) && - ker.wVirtualKeyCode == 'D') { - break; - } - } - SetConsoleMode(conin, consoleMode); -} - int main(int argc, char *argv[]) { if (argc == 2 && !strcmp(argv[1], "--version")) { @@ -95,8 +58,11 @@ int main(int argc, char *argv[]) return 0; } - if (argc == 2 && !strcmp(argv[1], "--showkey")) { - debugShowKey(); + if (argc == 2 && !strcmp(argv[1], "--show-input")) { + debugShowInput(false); + return 0; + } else if (argc == 3 && !strcmp(argv[1], "--show-input") && !strcmp(argv[2], "--with-mouse")) { + debugShowInput(true); return 0; } diff --git a/src/agent/subdir.mk b/src/agent/subdir.mk index eb52aa4..fd6a4ab 100644 --- a/src/agent/subdir.mk +++ b/src/agent/subdir.mk @@ -26,6 +26,7 @@ AGENT_OBJECTS = \ build/mingw/agent/ConsoleInput.o \ build/mingw/agent/ConsoleLine.o \ build/mingw/agent/Coord.o \ + build/mingw/agent/DebugShowInput.o \ build/mingw/agent/DefaultInputMap.o \ build/mingw/agent/EventLoop.o \ build/mingw/agent/InputMap.o \ diff --git a/src/winpty.gyp b/src/winpty.gyp index 017575f..e5150d1 100644 --- a/src/winpty.gyp +++ b/src/winpty.gyp @@ -38,6 +38,8 @@ 'agent/ConsoleLine.h', 'agent/Coord.h', 'agent/Coord.cc', + 'agent/DebugShowInput.h', + 'agent/DebugShowInput.cc', 'agent/DefaultInputMap.h', 'agent/DefaultInputMap.cc', 'agent/DsrSender.h',