diff options
Diffstat (limited to 'src/libs/3rdparty/winpty')
182 files changed, 24575 insertions, 0 deletions
diff --git a/src/libs/3rdparty/winpty/.gitattributes b/src/libs/3rdparty/winpty/.gitattributes new file mode 100644 index 0000000000..36d4c60f1a --- /dev/null +++ b/src/libs/3rdparty/winpty/.gitattributes @@ -0,0 +1,19 @@ +* text=auto +*.bat text eol=crlf +*.c text +*.cc text +*.gyp text +*.gypi text +*.h text +*.ps1 text eol=crlf +*.rst text +*.sh text +*.txt text +.gitignore text +.gitattributes text +Makefile text +configure text + +*.sh eol=lf +configure eol=lf +VERSION.txt eol=lf diff --git a/src/libs/3rdparty/winpty/.gitignore b/src/libs/3rdparty/winpty/.gitignore new file mode 100644 index 0000000000..68c6b47fb3 --- /dev/null +++ b/src/libs/3rdparty/winpty/.gitignore @@ -0,0 +1,16 @@ +*.sln +*.suo +*.vcxproj +*.vcxproj.filters +*.pyc +winpty.sdf +winpty.opensdf +/config.mk +/build +/build-gyp +/build-libpty +/ship/packages +/ship/tmp +/src/Default +/src/Release +/src/gen diff --git a/src/libs/3rdparty/winpty/CMakeLists.txt b/src/libs/3rdparty/winpty/CMakeLists.txt new file mode 100644 index 0000000000..febd4f0ab6 --- /dev/null +++ b/src/libs/3rdparty/winpty/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(src) diff --git a/src/libs/3rdparty/winpty/LICENSE b/src/libs/3rdparty/winpty/LICENSE new file mode 100644 index 0000000000..246fbe0113 --- /dev/null +++ b/src/libs/3rdparty/winpty/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011-2016 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. diff --git a/src/libs/3rdparty/winpty/README.md b/src/libs/3rdparty/winpty/README.md new file mode 100644 index 0000000000..bc8e7d6e5f --- /dev/null +++ b/src/libs/3rdparty/winpty/README.md @@ -0,0 +1,151 @@ +# winpty + +[![Build Status](https://ci.appveyor.com/api/projects/status/69tb9gylsph1ee1x/branch/master?svg=true)](https://ci.appveyor.com/project/rprichard/winpty/branch/master) + +winpty is a Windows software package providing an interface similar to a Unix +pty-master for communicating with Windows console programs. The package +consists of a library (libwinpty) and a tool for Cygwin and MSYS for running +Windows console programs in a Cygwin/MSYS pty. + +The software works by starting the `winpty-agent.exe` process with a new, +hidden console window, which bridges between the console API and terminal +input/output escape codes. It polls the hidden console's screen buffer for +changes and generates a corresponding stream of output. + +The Unix adapter allows running Windows console programs (e.g. CMD, PowerShell, +IronPython, etc.) under `mintty` or Cygwin's `sshd` with +properly-functioning input (e.g. arrow and function keys) and output (e.g. line +buffering). The library could be also useful for writing a non-Cygwin SSH +server. + +## Supported Windows versions + +winpty runs on Windows XP through Windows 10, including server versions. It +can be compiled into either 32-bit or 64-bit binaries. + +## Cygwin/MSYS adapter (`winpty.exe`) + +### Prerequisites + +You need the following to build winpty: + +* A Cygwin or MSYS installation +* GNU make +* A MinGW g++ toolchain capable of compiling C++11 code to build `winpty.dll` + and `winpty-agent.exe` +* A g++ toolchain targeting Cygwin or MSYS to build `winpty.exe` + +Winpty requires two g++ toolchains as it is split into two parts. The +`winpty.dll` and `winpty-agent.exe` binaries interface with the native +Windows command prompt window so they are compiled with the native MinGW +toolchain. The `winpty.exe` binary interfaces with the MSYS/Cygwin terminal so +it is compiled with the MSYS/Cygwin toolchain. + +MinGW appears to be split into two distributions -- MinGW (creates 32-bit +binaries) and MinGW-w64 (creates both 32-bit and 64-bit binaries). Either +one is generally acceptable. + +#### Cygwin packages + +The default g++ compiler for Cygwin targets Cygwin itself, but Cygwin also +packages MinGW-w64 compilers. As of this writing, the necessary packages are: + +* Either `mingw64-i686-gcc-g++` or `mingw64-x86_64-gcc-g++`. Select the + appropriate compiler for your CPU architecture. +* `gcc-g++` +* `make` + +As of this writing (2016-01-23), only the MinGW-w64 compiler is acceptable. +The MinGW compiler (e.g. from the `mingw-gcc-g++` package) is no longer +maintained and is too buggy. + +#### MSYS packages + +For the original MSYS, use the `mingw-get` tool (MinGW Installation Manager), +and select at least these components: + +* `mingw-developer-toolkit` +* `mingw32-base` +* `mingw32-gcc-g++` +* `msys-base` +* `msys-system-builder` + +When running `./configure`, make sure that `mingw32-g++` is in your +`PATH`. It will be in the `C:\MinGW\bin` directory. + +#### MSYS2 packages + +For MSYS2, use `pacman` and install at least these packages: + +* `msys/gcc` +* `mingw32/mingw-w64-i686-gcc` or `mingw64/mingw-w64-x86_64-gcc`. Select + the appropriate compiler for your CPU architecture. +* `make` + +MSYS2 provides three start menu shortcuts for starting MSYS2: + +* MinGW-w64 Win32 Shell +* MinGW-w64 Win64 Shell +* MSYS2 Shell + +To build winpty, use the MinGW-w64 {Win32,Win64} shortcut of the architecture +matching MSYS2. These shortcuts will put the g++ compiler from the +`{mingw32,mingw64}/mingw-w64-{i686,x86_64}-gcc` packages into the `PATH`. + +Alternatively, instead of installing `mingw32/mingw-w64-i686-gcc` or +`mingw64/mingw-w64-x86_64-gcc`, install the `mingw-w64-cross-gcc` and +`mingw-w64-cross-crt-git` packages. These packages install cross-compilers +into `/opt/bin`, and then any of the three shortcuts will work. + +### Building the Unix adapter + +In the project directory, run `./configure`, then `make`, then `make install`. +By default, winpty is installed into `/usr/local`. Pass `PREFIX=<path>` to +`make install` to override this default. + +### Using the Unix adapter + +To run a Windows console program in `mintty` or Cygwin `sshd`, prepend +`winpty` to the command-line: + + $ winpty powershell + Windows PowerShell + Copyright (C) 2009 Microsoft Corporation. All rights reserved. + + PS C:\rprichard\proj\winpty> 10 + 20 + 30 + PS C:\rprichard\proj\winpty> exit + +## Embedding winpty / MSVC compilation + +See `src/include/winpty.h` for the prototypes of functions exported by +`winpty.dll`. + +Only the `winpty.exe` binary uses Cygwin; all the other binaries work without +it and can be compiled with either MinGW or MSVC. To compile using MSVC, +download gyp and run `gyp -I configurations.gypi` in the `src` subdirectory. +This will generate a `winpty.sln` and associated project files. See the +`src/winpty.gyp` and `src/configurations.gypi` files for notes on dealing with +MSVC versions and different architectures. + +Compiling winpty with MSVC currently requires MSVC 2013 or newer. + +## Debugging winpty + +winpty comes with a tool for collecting timestamped debugging output. To use +it: + +1. Run `winpty-debugserver.exe` on the same computer as winpty. +2. Set the `WINPTY_DEBUG` environment variable to `trace` for the + `winpty.exe` process and/or the process using `libwinpty.dll`. + +winpty also recognizes a `WINPTY_SHOW_CONSOLE` environment variable. Set it +to 1 to prevent winpty from hiding the console window. + +## Copyright + +This project is distributed under the MIT license (see the `LICENSE` file in +the project root). + +By submitting a pull request for this project, you agree to license your +contribution under the MIT license to this project. diff --git a/src/libs/3rdparty/winpty/RELEASES.md b/src/libs/3rdparty/winpty/RELEASES.md new file mode 100644 index 0000000000..768cdf90e3 --- /dev/null +++ b/src/libs/3rdparty/winpty/RELEASES.md @@ -0,0 +1,280 @@ +# Next Version + +Input handling changes: + + * Improve Ctrl-C handling with programs that use unprocessed input. (e.g. + Ctrl-C now cancels input with PowerShell on Windows 10.) + [#116](https://github.com/rprichard/winpty/issues/116) + * Fix a theoretical issue with input event ordering. + [#117](https://github.com/rprichard/winpty/issues/117) + * Ctrl/Shift+{Arrow,Home,End} keys now work with IntelliJ. + [#118](https://github.com/rprichard/winpty/issues/118) + +# Version 0.4.3 (2017-05-17) + +Input handling changes: + + * winpty sets `ENHANCED_KEY` for arrow and navigation keys. This fixes an + issue with the Ruby REPL. + [#99](https://github.com/rprichard/winpty/issues/99) + * AltGr keys are handled better now. + [#109](https://github.com/rprichard/winpty/issues/109) + * In `ENABLE_VIRTUAL_TERMINAL_INPUT` mode, when typing Home/End with a + modifier (e.g. Ctrl), winpty now generates an H/F escape sequence like + `^[[1;5F` rather than a 1/4 escape like `^[[4;5~`. + [#114](https://github.com/rprichard/winpty/issues/114) + +Resizing and scraping fixes: + + * winpty now synthesizes a `WINDOW_BUFFER_SIZE_EVENT` event after resizing + the console to better propagate window size changes to console programs. + In particular, this affects WSL and Cygwin. + [#110](https://github.com/rprichard/winpty/issues/110) + * Better handling of resizing for certain full-screen programs, like + WSL less. + [#112](https://github.com/rprichard/winpty/issues/112) + * Hide the cursor if it's currently outside the console window. This change + fixes an issue with Far Manager. + [#113](https://github.com/rprichard/winpty/issues/113) + * winpty now avoids using console fonts smaller than 5px high to improve + half-vs-full-width character handling. See + https://github.com/Microsoft/vscode/issues/19665. + [b4db322010](https://github.com/rprichard/winpty/commit/b4db322010d2d897e6c496fefc4f0ecc9b84c2f3) + +Cygwin/MSYS adapter fix: + + * The way the `winpty` Cygwin/MSYS2 adapter searches for the program to + launch changed. It now resolves symlinks and searches the PATH explicitly. + [#81](https://github.com/rprichard/winpty/issues/81) + [#98](https://github.com/rprichard/winpty/issues/98) + +This release does not include binaries for the old MSYS1 project anymore. +MSYS2 will continue to be supported. See +https://github.com/rprichard/winpty/issues/97. + +# Version 0.4.2 (2017-01-18) + +This release improves WSL support (i.e. Bash-on-Windows): + + * winpty generates more correct input escape sequences for WSL programs that + enable an alternate input mode using DECCKM. This bug affected arrow keys + and Home/End in WSL programs such as `vim`, `mc`, and `less`. + [#90](https://github.com/rprichard/winpty/issues/90) + * winpty now recognizes the `COMMON_LVB_REVERSE_VIDEO` and + `COMMON_LVB_UNDERSCORE` text attributes. The Windows console uses these + attributes to implement the SGR.4(Underline) and SGR.7(Negative) modes in + its VT handling. This change affects WSL pager status bars, man pages, etc. + +The build system no longer has a "version suffix" mechanism, so passing +`VERSION_SUFFIX=<suffix>` to make or `-D VERSION_SUFFIX=<suffix>` to gyp now +has no effect. AFAIK, the mechanism was never used publicly. +[67a34b6c03](https://github.com/rprichard/winpty/commit/67a34b6c03557a5c2e0a2bdd502c2210921d8f3e) + +# Version 0.4.1 (2017-01-03) + +Bug fixes: + + * This version fixes a bug where the `winpty-agent.exe` process could read + past the end of a buffer. + [#94](https://github.com/rprichard/winpty/issues/94) + +# Version 0.4.0 (2016-06-28) + +The winpty library has a new API that should be easier for embedding. +[880c00c69e](https://github.com/rprichard/winpty/commit/880c00c69eeca73643ddb576f02c5badbec81f56) + +User-visible changes: + + * winpty now automatically puts the terminal into mouse mode when it detects + that the console has left QuickEdit mode. The `--mouse` option still forces + the terminal into mouse mode. In principle, an option could be added to + suppress terminal mode, but hopefully it won't be necessary. There is a + script in the `misc` subdirectory, `misc/ConinMode.ps1`, that can change + the QuickEdit mode from the command-line. + * winpty now passes keyboard escapes to `bash.exe` in the Windows Subsystem + for Linux. + [#82](https://github.com/rprichard/winpty/issues/82) + +Bug fixes: + + * By default, `winpty.dll` avoids calling `SetProcessWindowStation` within + the calling process. + [#58](https://github.com/rprichard/winpty/issues/58) + * Fixed an uninitialized memory bug that could have crashed winpty. + [#80](https://github.com/rprichard/winpty/issues/80) + * winpty now works better with very large and very small terminal windows. + It resizes the console font according to the number of columns. + [#61](https://github.com/rprichard/winpty/issues/61) + * winpty no longer uses Mark to freeze the console on Windows 10. The Mark + command could interfere with the cursor position, corrupting the data in + the screen buffer. + [#79](https://github.com/rprichard/winpty/issues/79) + +# Version 0.3.0 (2016-05-20) + +User-visible changes: + + * The UNIX adapter is renamed from `console.exe` to `winpty.exe` to be + consistent with MSYS2. The name `winpty.exe` is less likely to conflict + with another program and is easier to search for online (e.g. for someone + unfamiliar with winpty). + * The UNIX adapter now clears the `TERM` variable. + [#43](https://github.com/rprichard/winpty/issues/43) + * An escape character appearing in a console screen buffer cell is converted + to a '?'. + [#47](https://github.com/rprichard/winpty/issues/47) + +Bug fixes: + + * A major bug affecting XP users was fixed. + [#67](https://github.com/rprichard/winpty/issues/67) + * Fixed an incompatibility with ConEmu where winpty hung if ConEmu's + "Process 'start'" feature was enabled. + [#70](https://github.com/rprichard/winpty/issues/70) + * Fixed a bug where `cmd.exe` sometimes printed the message, + `Not enough storage is available to process this command.`. + [#74](https://github.com/rprichard/winpty/issues/74) + +Many changes internally: + + * The codebase is switched from C++03 to C++11 and uses exceptions internally. + No exceptions are thrown across the C APIs defined in `winpty.h`. + * This version drops support for the original MinGW compiler packaged with + Cygwin (`i686-pc-mingw32-g++`). The MinGW-w64 compiler is still supported, + as is the MinGW distributed at mingw.org. Compiling with MSVC now requires + MSVC 2013 or newer. Windows XP is still supported. + [ec3eae8df5](https://github.com/rprichard/winpty/commit/ec3eae8df5bbbb36d7628d168b0815638d122f37) + * Pipe security is improved. winpty works harder to produce unique pipe names + and includes a random component in the name. winpty secures pipes with a + DACL that prevents arbitrary users from connecting to its pipes. winpty now + passes `PIPE_REJECT_REMOTE_CLIENTS` on Vista and up, and it verifies that + the pipe client PID is correct, again on Vista and up. When connecting to a + named pipe, winpty uses the `SECURITY_IDENTIFICATION` flag to restrict + impersonation. Previous versions *should* still be secure. + * `winpty-debugserver.exe` now has an `--everyone` flag that allows capturing + debug output from other users. + * The code now compiles cleanly with MSVC's "Security Development Lifecycle" + (`/SDL`) checks enabled. + +# Version 0.2.2 (2016-02-25) + +Minor bug fixes and enhancements: + + * Fix a bug that generated spurious mouse input records when an incomplete + mouse escape sequence was seen. + * Fix a buffer overflow bug in `winpty-debugserver.exe` affecting messages of + exactly 4096 bytes. + * For MSVC builds, add a `src/configurations.gypi` file that can be included + on the gyp command-line to enable 32-bit and 64-bit builds. + * `winpty-agent --show-input` mode: Flush stdout after each line. + * Makefile builds: generate a `build/winpty.lib` import library to accompany + `build/winpty.dll`. + +# Version 0.2.1 (2015-12-19) + + * The main project source was moved into a `src` directory for better code + organization and to fix + [#51](https://github.com/rprichard/winpty/issues/51). + * winpty recognizes many more escape sequences, including: + * putty/rxvt's F1-F4 keys + [#40](https://github.com/rprichard/winpty/issues/40) + * the Linux virtual console's F1-F5 keys + * the "application numpad" keys (e.g. enabled with DECPAM) + * Fixed handling of Shift-Alt-O and Alt-[. + * Added support for mouse input. The UNIX adapter has a `--mouse` argument + that puts the terminal into mouse mode, but the agent recognizes mouse + input even without the argument. The agent recognizes double-clicks using + Windows' double-click interval setting (i.e. GetDoubleClickTime). + [#57](https://github.com/rprichard/winpty/issues/57) + +Changes to debugging interfaces: + + * The `WINPTY_DEBUG` variable is now a comma-separated list. The old + behavior (i.e. tracing) is enabled with `WINPTY_DEBUG=trace`. + * The UNIX adapter program now has a `--showkey` argument that dumps input + bytes. + * The `winpty-agent.exe` program has a `--show-input` argument that dumps + `INPUT_RECORD` records. (It omits mouse events unless `--with-mouse` is + also specified.) The agent also responds to `WINPTY_DEBUG=trace,input`, + which logs input bytes and synthesized console events, and it responds to + `WINPTY_DEBUG=trace,dump_input_map`, which dumps the internal table of + escape sequences. + +# Version 0.2.0 (2015-11-13) + +No changes to the API, but many small changes to the implementation. The big +changes include: + + * Support for 64-bit Cygwin and MSYS2 + * Support for Windows 10 + * Better Unicode support (especially East Asian languages) + +Details: + + * The `configure` script recognizes 64-bit Cygwin and MSYS2 environments and + selects the appropriate compiler. + * winpty works much better with the upgraded console in Windows 10. The + `conhost.exe` hang can still occur, but only with certain programs, and + is much less likely to occur. With the new console, use Mark instead of + SelectAll, for better performance. + [#31](https://github.com/rprichard/winpty/issues/31) + [#30](https://github.com/rprichard/winpty/issues/30) + [#53](https://github.com/rprichard/winpty/issues/53) + * The UNIX adapter now calls `setlocale(LC_ALL, "")` to set the locale. + * Improved Unicode support. When a console is started with an East Asian code + page, winpty now chooses an East Asian font rather than Consolas / Lucida + Console. Selecting the right font helps synchronize character widths + between the console and terminal. (It's not perfect, though.) + [#41](https://github.com/rprichard/winpty/issues/41) + * winpty now more-or-less works with programs that change the screen buffer + or resize the original screen buffer. If the screen buffer height changes, + winpty switches to a "direct mode", where it makes no effort to track + scrolling. In direct mode, it merely syncs snapshots of the console to the + terminal. Caveats: + * Changing the screen buffer (i.e. `SetConsoleActiveScreenBuffer`) + breaks winpty on Windows 7. This problem can eventually be mitigated, + but never completely fixed, due to Windows 7 bugginess. + * Resizing the original screen buffer can hang `conhost.exe` on Windows 10. + Enabling the legacy console is a workaround. + * If a program changes the screen buffer and then exits, relying on the OS + to restore the original screen buffer, that restoration probably will not + happen with winpty. winpty's behavior can probably be improved here. + * Improved color handling: + * DkGray-on-Black text was previously hiddenly completely. Now it is + output as DkGray, with a fallback to LtGray on terminals that don't + recognize the intense colors. + [#39](https://github.com/rprichard/winpty/issues/39). + * The console is always initialized to LtGray-on-Black, regardless of the + user setting, which matches the console color heuristic, which translates + LtGray-on-Black to "reset SGR parameters." + * Shift-Tab is recognized correctly now. + [#19](https://github.com/rprichard/winpty/issues/19) + * Add a `--version` argument to `winpty-agent.exe` and the UNIX adapter. The + argument reports the nominal version (i.e. the `VERSION.txt`) file, with a + "VERSION_SUFFIX" appended (defaulted to `-dev`), and a git commit hash, if + the `git` command successfully reports a hash during the build. The `git` + command is invoked by either `make` or `gyp`. + * The agent now combines `ReadConsoleOutputW` calls when it polls the console + buffer for changes, which may slightly reduce its CPU overhead. + [#44](https://github.com/rprichard/winpty/issues/44). + * A `gyp` file is added to help compile with MSVC. + * The code can now be compiled as C++11 code, though it isn't by default. + [bde8922e08](https://github.com/rprichard/winpty/commit/bde8922e08c3638e01ecc7b581b676c314163e3c) + * If winpty can't create a new window station, it charges ahead rather than + aborting. This situation might happen if winpty were started from an SSH + session. + * Debugging improvements: + * `WINPTYDBG` is renamed to `WINPTY_DEBUG`, and a new `WINPTY_SHOW_CONSOLE` + variable keeps the underlying console visible. + * A `winpty-debugserver.exe` program is built and shipped by default. It + collects the trace output enabled with `WINPTY_DEBUG`. + * The `Makefile` build of winpty now compiles `winpty-agent.exe` and + `winpty.dll` with -O2. + +# Version 0.1.1 (2012-07-28) + +Minor bugfix release. + +# Version 0.1 (2012-04-17) + +Initial release. diff --git a/src/libs/3rdparty/winpty/VERSION.txt b/src/libs/3rdparty/winpty/VERSION.txt new file mode 100644 index 0000000000..5d47ff8c45 --- /dev/null +++ b/src/libs/3rdparty/winpty/VERSION.txt @@ -0,0 +1 @@ +0.4.4-dev diff --git a/src/libs/3rdparty/winpty/appveyor.yml b/src/libs/3rdparty/winpty/appveyor.yml new file mode 100644 index 0000000000..a9e8726fc1 --- /dev/null +++ b/src/libs/3rdparty/winpty/appveyor.yml @@ -0,0 +1,16 @@ +image: Visual Studio 2015 + +init: + - C:\msys64\usr\bin\bash --login -c "pacman -S --needed --noconfirm --noprogressbar msys/make msys/tar msys/gcc mingw-w64-cross-toolchain" + - C:\cygwin\setup-x86 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make + - C:\cygwin64\setup-x86_64 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make + +build_script: + - C:\Python27-x64\python.exe ship\ship.py --kind msys2 --arch x64 --syspath C:\msys64 + - C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch ia32 --syspath C:\cygwin + - C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch x64 --syspath C:\cygwin64 + - C:\Python27-x64\python.exe ship\make_msvc_package.py + +artifacts: + - path: ship\packages\*.tar.gz + - path: ship\packages\*.zip diff --git a/src/libs/3rdparty/winpty/configure b/src/libs/3rdparty/winpty/configure new file mode 100644 index 0000000000..6d37d65b09 --- /dev/null +++ b/src/libs/3rdparty/winpty/configure @@ -0,0 +1,167 @@ +#!/bin/bash +# +# Copyright (c) 2011-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. + +# +# findTool(desc, commandList) +# +# Searches commandLine for the first command in the PATH and returns it. +# Prints an error and aborts the script if no match is found. +# +FINDTOOL_OUT="" +function findTool { + DESC=$1 + OPTIONS=$2 + for CMD in ${OPTIONS}; do + if (which $CMD &>/dev/null) then + echo "Found $DESC: $CMD" + FINDTOOL_OUT="$CMD" + return + fi + done + echo "Error: could not find $DESC. One of these should be in your PATH:" + for CMD in ${OPTIONS}; do + echo " * $CMD" + done + exit 1 +} + +IS_CYGWIN=0 +IS_MSYS1=0 +IS_MSYS2=0 + +# Link parts of the Cygwin binary statically to aid in redistribution? The +# binary still links dynamically against the main DLL. The MinGW binaries are +# also statically linked and therefore depend only on Windows DLLs. I started +# linking the Cygwin/MSYS binary statically, because G++ 4.7 changed the +# Windows C++ ABI. +UNIX_LDFLAGS_STATIC='-static -static-libgcc -static-libstdc++' + +# Detect the environment -- Cygwin or MSYS. +case $(uname -s) in + CYGWIN*) + echo 'uname -s identifies a Cygwin environment.' + IS_CYGWIN=1 + case $(uname -m) in + i686) + echo 'uname -m identifies an i686 environment.' + UNIX_CXX=i686-pc-cygwin-g++ + MINGW_CXX=i686-w64-mingw32-g++ + ;; + x86_64) + echo 'uname -m identifies an x86_64 environment.' + UNIX_CXX=x86_64-pc-cygwin-g++ + MINGW_CXX=x86_64-w64-mingw32-g++ + ;; + *) + echo 'Error: uname -m did not match either i686 or x86_64.' + exit 1 + ;; + esac + ;; + MSYS*|MINGW*) + # MSYS2 notes: + # - MSYS2 offers two shortcuts to open an environment: + # - MinGW-w64 Win32 Shell. This env reports a `uname -s` of + # MINGW32_NT-6.1 on 32-bit Win7. The MinGW-w64 compiler + # (i686-w64-mingw32-g++.exe) is in the PATH. + # - MSYS2 Shell. `uname -s` instead reports MSYS_NT-6.1. + # The i686-w64-mingw32-g++ compiler is not in the PATH. + # - MSYS2 appears to use MinGW-w64, not the older mingw.org. + # MSYS notes: + # - `uname -s` is always MINGW32_NT-6.1 on Win7. + echo 'uname -s identifies an MSYS/MSYS2 environment.' + case $(uname -m) in + i686) + echo 'uname -m identifies an i686 environment.' + UNIX_CXX=i686-pc-msys-g++ + if echo "$(uname -r)" | grep '^1[.]' > /dev/null; then + # The MSYS-targeting compiler for the original 32-bit-only + # MSYS does not recognize the -static-libstdc++ flag, and + # it does not work with -static, because it tries to link + # statically with the core MSYS library and fails. + # + # Distinguish between the two using the major version + # number of `uname -r`: + # + # MSYS uname -r: 1.0.18(0.48/3/2) + # MSYS2 uname -r: 2.0.0(0.284/5/3) + # + # This is suboptimal because MSYS2 is not actually the + # second version of MSYS--it's a brand-new fork of Cygwin. + # + IS_MSYS1=1 + UNIX_LDFLAGS_STATIC= + MINGW_CXX=mingw32-g++ + else + IS_MSYS2=1 + MINGW_CXX=i686-w64-mingw32-g++.exe + fi + ;; + x86_64) + echo 'uname -m identifies an x86_64 environment.' + IS_MSYS2=1 + UNIX_CXX=x86_64-pc-msys-g++ + MINGW_CXX=x86_64-w64-mingw32-g++ + ;; + *) + echo 'Error: uname -m did not match either i686 or x86_64.' + exit 1 + ;; + esac + ;; + *) + echo 'Error: uname -s did not match either CYGWIN* or MINGW*.' + exit 1 + ;; +esac + +# Search the PATH and pick the first match. +findTool "Cygwin/MSYS G++ compiler" "$UNIX_CXX" +UNIX_CXX=$FINDTOOL_OUT +findTool "MinGW G++ compiler" "$MINGW_CXX" +MINGW_CXX=$FINDTOOL_OUT + +# Write config files. +echo Writing config.mk +echo UNIX_CXX=$UNIX_CXX > config.mk +echo UNIX_LDFLAGS_STATIC=$UNIX_LDFLAGS_STATIC >> config.mk +echo MINGW_CXX=$MINGW_CXX >> config.mk + +if test $IS_MSYS1 = 1; then + echo UNIX_CXXFLAGS += -DWINPTY_TARGET_MSYS1 >> config.mk + # The MSYS1 MinGW compiler has a bug that prevents inclusion of algorithm + # and math.h in normal C++11 mode. The workaround is to enable the gnu++11 + # mode instead. The bug was fixed on 2015-07-31, but as of 2016-02-26, the + # fix apparently hasn't been released. See + # http://ehc.ac/p/mingw/bugs/2250/. + echo MINGW_ENABLE_CXX11_FLAG := -std=gnu++11 >> config.mk +fi + +if test -d .git -a -f .git/HEAD -a -f .git/index && git rev-parse HEAD >&/dev/null; then + echo "Commit info: git" + echo 'COMMIT_HASH = $(shell git rev-parse HEAD)' >> config.mk + echo 'COMMIT_HASH_DEP := config.mk .git/HEAD .git/index' >> config.mk +else + echo "Commit info: none" + echo 'COMMIT_HASH := none' >> config.mk + echo 'COMMIT_HASH_DEP := config.mk' >> config.mk +fi diff --git a/src/libs/3rdparty/winpty/misc/.gitignore b/src/libs/3rdparty/winpty/misc/.gitignore new file mode 100644 index 0000000000..23751645fa --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/.gitignore @@ -0,0 +1,2 @@ +*.exe +UnixEcho
\ No newline at end of file diff --git a/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc b/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc new file mode 100644 index 0000000000..a5bb074826 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc @@ -0,0 +1,90 @@ +#include <windows.h> +#include <cassert> + +#include "TestUtil.cc" + +void dumpInfoToTrace() { + CONSOLE_SCREEN_BUFFER_INFO info; + assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info)); + trace("win=(%d,%d,%d,%d)", + (int)info.srWindow.Left, + (int)info.srWindow.Top, + (int)info.srWindow.Right, + (int)info.srWindow.Bottom); + trace("buf=(%d,%d)", + (int)info.dwSize.X, + (int)info.dwSize.Y); + trace("cur=(%d,%d)", + (int)info.dwCursorPosition.X, + (int)info.dwCursorPosition.Y); +} + +int main(int argc, char *argv[]) { + if (argc == 1) { + startChildProcess(L"CHILD"); + return 0; + } + + setWindowPos(0, 0, 1, 1); + + if (false) { + // Reducing the buffer height can move the window up. + setBufferSize(80, 25); + setWindowPos(0, 20, 80, 5); + Sleep(2000); + setBufferSize(80, 10); + } + + if (false) { + // Reducing the buffer height moves the window up and the buffer + // contents up too. + setBufferSize(80, 25); + setWindowPos(0, 20, 80, 5); + setCursorPos(0, 20); + printf("TEST1\nTEST2\nTEST3\nTEST4\n"); + fflush(stdout); + Sleep(2000); + setBufferSize(80, 10); + } + + if (false) { + // Reducing the buffer width can move the window left. + setBufferSize(80, 25); + setWindowPos(40, 0, 40, 25); + Sleep(2000); + setBufferSize(60, 25); + } + + if (false) { + // Sometimes the buffer contents are shifted up; sometimes they're + // shifted down. It seems to depend on the cursor position? + + // setBufferSize(80, 25); + // setWindowPos(0, 20, 80, 5); + // setCursorPos(0, 20); + // printf("TESTa\nTESTb\nTESTc\nTESTd\nTESTe"); + // fflush(stdout); + // setCursorPos(0, 0); + // printf("TEST1\nTEST2\nTEST3\nTEST4\nTEST5"); + // fflush(stdout); + // setCursorPos(0, 24); + // Sleep(5000); + // setBufferSize(80, 24); + + setBufferSize(80, 20); + setWindowPos(0, 10, 80, 10); + setCursorPos(0, 18); + + printf("TEST1\nTEST2"); + fflush(stdout); + setCursorPos(0, 18); + + Sleep(2000); + setBufferSize(80, 18); + } + + dumpInfoToTrace(); + Sleep(30000); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc b/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc new file mode 100644 index 0000000000..701a2cb4a3 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc @@ -0,0 +1,53 @@ +// A test program for CreateConsoleScreenBuffer / SetConsoleActiveScreenBuffer +// + +#include <windows.h> +#include <stdio.h> +#include <conio.h> +#include <io.h> +#include <cassert> + +#include "TestUtil.cc" + +int main() +{ + HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE childBuffer = CreateConsoleScreenBuffer( + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CONSOLE_TEXTMODE_BUFFER, NULL); + + SetConsoleActiveScreenBuffer(childBuffer); + + while (true) { + char buf[1024]; + CONSOLE_SCREEN_BUFFER_INFO info; + + assert(GetConsoleScreenBufferInfo(origBuffer, &info)); + trace("child.size=(%d,%d)", (int)info.dwSize.X, (int)info.dwSize.Y); + trace("child.cursor=(%d,%d)", (int)info.dwCursorPosition.X, (int)info.dwCursorPosition.Y); + trace("child.window=(%d,%d,%d,%d)", + (int)info.srWindow.Left, (int)info.srWindow.Top, + (int)info.srWindow.Right, (int)info.srWindow.Bottom); + trace("child.maxSize=(%d,%d)", (int)info.dwMaximumWindowSize.X, (int)info.dwMaximumWindowSize.Y); + + int ch = getch(); + sprintf(buf, "%02x\n", ch); + DWORD actual = 0; + WriteFile(childBuffer, buf, strlen(buf), &actual, NULL); + if (ch == 0x1b/*ESC*/ || ch == 0x03/*CTRL-C*/) + break; + + if (ch == 'b') { + setBufferSize(origBuffer, 40, 25); + } else if (ch == 'w') { + setWindowPos(origBuffer, 1, 1, 38, 23); + } else if (ch == 'c') { + setCursorPos(origBuffer, 10, 10); + } + } + + SetConsoleActiveScreenBuffer(origBuffer); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ClearConsole.cc b/src/libs/3rdparty/winpty/misc/ClearConsole.cc new file mode 100644 index 0000000000..f95f8c84ca --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ClearConsole.cc @@ -0,0 +1,72 @@ +/* + * Demonstrates that console clearing sets each cell's character to SP, not + * NUL, and it sets the attribute of each cell to the current text attribute. + * + * This confirms the MSDN instruction in the "Clearing the Screen" article. + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682022(v=vs.85).aspx + * It advises using GetConsoleScreenBufferInfo to get the current text + * attribute, then FillConsoleOutputCharacter and FillConsoleOutputAttribute to + * write to the console buffer. + */ + +#include <windows.h> + +#include <cassert> +#include <cstdio> +#include <cstdlib> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + if (argc == 1) { + startChildProcess(L"CHILD"); + return 0; + } + + const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + + SetConsoleTextAttribute(conout, 0x24); + system("cls"); + + setWindowPos(0, 0, 1, 1); + setBufferSize(80, 25); + setWindowPos(0, 0, 80, 25); + + CHAR_INFO buf; + COORD bufSize = { 1, 1 }; + COORD bufCoord = { 0, 0 }; + SMALL_RECT rect = { 5, 5, 5, 5 }; + BOOL ret; + DWORD actual; + COORD writeCoord = { 5, 5 }; + + // After cls, each cell's character is a space, and its attributes are the + // default text attributes. + ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect); + assert(ret && buf.Char.UnicodeChar == L' ' && buf.Attributes == 0x24); + + // Nevertheless, it is possible to change a cell to NUL. + ret = FillConsoleOutputCharacterW(conout, L'\0', 1, writeCoord, &actual); + assert(ret && actual == 1); + ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect); + assert(ret && buf.Char.UnicodeChar == L'\0' && buf.Attributes == 0x24); + + // As well as a 0 attribute. (As one would expect, the cell is + // black-on-black.) + ret = FillConsoleOutputAttribute(conout, 0, 1, writeCoord, &actual); + assert(ret && actual == 1); + ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect); + assert(ret && buf.Char.UnicodeChar == L'\0' && buf.Attributes == 0); + ret = FillConsoleOutputCharacterW(conout, L'X', 1, writeCoord, &actual); + assert(ret && actual == 1); + ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect); + assert(ret && buf.Char.UnicodeChar == L'X' && buf.Attributes == 0); + + // The 'X' is invisible. + countDown(3); + + ret = FillConsoleOutputAttribute(conout, 0x42, 1, writeCoord, &actual); + assert(ret && actual == 1); + + countDown(5); +} diff --git a/src/libs/3rdparty/winpty/misc/ConinMode.cc b/src/libs/3rdparty/winpty/misc/ConinMode.cc new file mode 100644 index 0000000000..1e1428d8b0 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ConinMode.cc @@ -0,0 +1,117 @@ +#include <windows.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> +#include <vector> + +static HANDLE getConin() { + HANDLE conin = GetStdHandle(STD_INPUT_HANDLE); + if (conin == INVALID_HANDLE_VALUE) { + fprintf(stderr, "error: cannot get stdin\n"); + exit(1); + } + return conin; +} + +static DWORD getConsoleMode() { + DWORD mode = 0; + if (!GetConsoleMode(getConin(), &mode)) { + fprintf(stderr, "error: GetConsoleMode failed (is stdin a console?)\n"); + exit(1); + } + return mode; +} + +static void setConsoleMode(DWORD mode) { + if (!SetConsoleMode(getConin(), mode)) { + fprintf(stderr, "error: SetConsoleMode failed (is stdin a console?)\n"); + exit(1); + } +} + +static long parseInt(const std::string &s) { + errno = 0; + char *endptr = nullptr; + long result = strtol(s.c_str(), &endptr, 0); + if (errno != 0 || !endptr || *endptr != '\0') { + fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str()); + exit(1); + } + return result; +} + +static void usage() { + printf("Usage: ConinMode [verb] [options]\n"); + printf("Verbs:\n"); + printf(" [info] Dumps info about mode flags.\n"); + printf(" get Prints the mode DWORD.\n"); + printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n"); + printf(" set VALUE MASK\n"); + printf(" Same as `set VALUE`, but only alters the bits in MASK.\n"); + exit(1); +} + +struct { + const char *name; + DWORD value; +} kInputFlags[] = { + "ENABLE_PROCESSED_INPUT", ENABLE_PROCESSED_INPUT, // 0x0001 + "ENABLE_LINE_INPUT", ENABLE_LINE_INPUT, // 0x0002 + "ENABLE_ECHO_INPUT", ENABLE_ECHO_INPUT, // 0x0004 + "ENABLE_WINDOW_INPUT", ENABLE_WINDOW_INPUT, // 0x0008 + "ENABLE_MOUSE_INPUT", ENABLE_MOUSE_INPUT, // 0x0010 + "ENABLE_INSERT_MODE", ENABLE_INSERT_MODE, // 0x0020 + "ENABLE_QUICK_EDIT_MODE", ENABLE_QUICK_EDIT_MODE, // 0x0040 + "ENABLE_EXTENDED_FLAGS", ENABLE_EXTENDED_FLAGS, // 0x0080 + "ENABLE_VIRTUAL_TERMINAL_INPUT", 0x0200/*ENABLE_VIRTUAL_TERMINAL_INPUT*/, // 0x0200 +}; + +int main(int argc, char *argv[]) { + std::vector<std::string> args; + for (size_t i = 1; i < argc; ++i) { + args.push_back(argv[i]); + } + + if (args.empty() || args.size() == 1 && args[0] == "info") { + DWORD mode = getConsoleMode(); + printf("mode: 0x%lx\n", mode); + for (const auto &flag : kInputFlags) { + printf("%-29s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off"); + mode &= ~flag.value; + } + for (int i = 0; i < 32; ++i) { + if (mode & (1u << i)) { + printf("Unrecognized flag: %04x\n", (1u << i)); + } + } + return 0; + } + + const auto verb = args[0]; + + if (verb == "set") { + if (args.size() == 2) { + const DWORD newMode = parseInt(args[1]); + setConsoleMode(newMode); + } else if (args.size() == 3) { + const DWORD mode = parseInt(args[1]); + const DWORD mask = parseInt(args[2]); + const int newMode = (getConsoleMode() & ~mask) | (mode & mask); + setConsoleMode(newMode); + } else { + usage(); + } + } else if (verb == "get") { + if (args.size() != 1) { + usage(); + } + printf("0x%lx\n", getConsoleMode()); + } else { + usage(); + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ConinMode.ps1 b/src/libs/3rdparty/winpty/misc/ConinMode.ps1 new file mode 100644 index 0000000000..ecfe8f039e --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ConinMode.ps1 @@ -0,0 +1,116 @@ +# +# PowerShell script for controlling the console QuickEdit and InsertMode flags. +# +# Turn QuickEdit off to interact with mouse-driven console programs. +# +# Usage: +# +# powershell .\ConinMode.ps1 [Options] +# +# Options: +# -QuickEdit [on/off] +# -InsertMode [on/off] +# -Mode [integer] +# + +param ( + [ValidateSet("on", "off")][string] $QuickEdit, + [ValidateSet("on", "off")][string] $InsertMode, + [int] $Mode +) + +$signature = @' +[DllImport("kernel32.dll", SetLastError = true)] +public static extern IntPtr GetStdHandle(int nStdHandle); + +[DllImport("kernel32.dll", SetLastError = true)] +public static extern uint GetConsoleMode( + IntPtr hConsoleHandle, + out uint lpMode); + +[DllImport("kernel32.dll", SetLastError = true)] +public static extern uint SetConsoleMode( + IntPtr hConsoleHandle, + uint dwMode); + +public const int STD_INPUT_HANDLE = -10; +public const int ENABLE_INSERT_MODE = 0x0020; +public const int ENABLE_QUICK_EDIT_MODE = 0x0040; +public const int ENABLE_EXTENDED_FLAGS = 0x0080; +'@ + +$WinAPI = Add-Type -MemberDefinition $signature ` + -Name WinAPI -Namespace ConinModeScript ` + -PassThru + +function GetConIn { + $ret = $WinAPI::GetStdHandle($WinAPI::STD_INPUT_HANDLE) + if ($ret -eq -1) { + throw "error: cannot get stdin" + } + return $ret +} + +function GetConsoleMode { + $conin = GetConIn + $mode = 0 + $ret = $WinAPI::GetConsoleMode($conin, [ref]$mode) + if ($ret -eq 0) { + throw "GetConsoleMode failed (is stdin a console?)" + } + return $mode +} + +function SetConsoleMode($mode) { + $conin = GetConIn + $ret = $WinAPI::SetConsoleMode($conin, $mode) + if ($ret -eq 0) { + throw "SetConsoleMode failed (is stdin a console?)" + } +} + +$oldMode = GetConsoleMode +$newMode = $oldMode +$doingSomething = $false + +if ($PSBoundParameters.ContainsKey("Mode")) { + $newMode = $Mode + $doingSomething = $true +} + +if ($QuickEdit + $InsertMode -ne "") { + if (!($newMode -band $WinAPI::ENABLE_EXTENDED_FLAGS)) { + # We can't enable an extended flag without overwriting the existing + # QuickEdit/InsertMode flags. AFAICT, there is no way to query their + # existing values, so at least we can choose sensible defaults. + $newMode = $newMode -bor $WinAPI::ENABLE_EXTENDED_FLAGS + $newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE + $newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE + $doingSomething = $true + } +} + +if ($QuickEdit -eq "on") { + $newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE + $doingSomething = $true +} elseif ($QuickEdit -eq "off") { + $newMode = $newMode -band (-bnot $WinAPI::ENABLE_QUICK_EDIT_MODE) + $doingSomething = $true +} + +if ($InsertMode -eq "on") { + $newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE + $doingSomething = $true +} elseif ($InsertMode -eq "off") { + $newMode = $newMode -band (-bnot $WinAPI::ENABLE_INSERT_MODE) + $doingSomething = $true +} + +if ($doingSomething) { + echo "old mode: $oldMode" + SetConsoleMode $newMode + $newMode = GetConsoleMode + echo "new mode: $newMode" +} else { + echo "mode: $oldMode" +} diff --git a/src/libs/3rdparty/winpty/misc/ConoutMode.cc b/src/libs/3rdparty/winpty/misc/ConoutMode.cc new file mode 100644 index 0000000000..100e0c7bea --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ConoutMode.cc @@ -0,0 +1,113 @@ +#include <windows.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> +#include <vector> + +static HANDLE getConout() { + HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + if (conout == INVALID_HANDLE_VALUE) { + fprintf(stderr, "error: cannot get stdout\n"); + exit(1); + } + return conout; +} + +static DWORD getConsoleMode() { + DWORD mode = 0; + if (!GetConsoleMode(getConout(), &mode)) { + fprintf(stderr, "error: GetConsoleMode failed (is stdout a console?)\n"); + exit(1); + } + return mode; +} + +static void setConsoleMode(DWORD mode) { + if (!SetConsoleMode(getConout(), mode)) { + fprintf(stderr, "error: SetConsoleMode failed (is stdout a console?)\n"); + exit(1); + } +} + +static long parseInt(const std::string &s) { + errno = 0; + char *endptr = nullptr; + long result = strtol(s.c_str(), &endptr, 0); + if (errno != 0 || !endptr || *endptr != '\0') { + fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str()); + exit(1); + } + return result; +} + +static void usage() { + printf("Usage: ConoutMode [verb] [options]\n"); + printf("Verbs:\n"); + printf(" [info] Dumps info about mode flags.\n"); + printf(" get Prints the mode DWORD.\n"); + printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n"); + printf(" set VALUE MASK\n"); + printf(" Same as `set VALUE`, but only alters the bits in MASK.\n"); + exit(1); +} + +struct { + const char *name; + DWORD value; +} kOutputFlags[] = { + "ENABLE_PROCESSED_OUTPUT", ENABLE_PROCESSED_OUTPUT, // 0x0001 + "ENABLE_WRAP_AT_EOL_OUTPUT", ENABLE_WRAP_AT_EOL_OUTPUT, // 0x0002 + "ENABLE_VIRTUAL_TERMINAL_PROCESSING", 0x0004/*ENABLE_VIRTUAL_TERMINAL_PROCESSING*/, // 0x0004 + "DISABLE_NEWLINE_AUTO_RETURN", 0x0008/*DISABLE_NEWLINE_AUTO_RETURN*/, // 0x0008 + "ENABLE_LVB_GRID_WORLDWIDE", 0x0010/*ENABLE_LVB_GRID_WORLDWIDE*/, //0x0010 +}; + +int main(int argc, char *argv[]) { + std::vector<std::string> args; + for (size_t i = 1; i < argc; ++i) { + args.push_back(argv[i]); + } + + if (args.empty() || args.size() == 1 && args[0] == "info") { + DWORD mode = getConsoleMode(); + printf("mode: 0x%lx\n", mode); + for (const auto &flag : kOutputFlags) { + printf("%-34s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off"); + mode &= ~flag.value; + } + for (int i = 0; i < 32; ++i) { + if (mode & (1u << i)) { + printf("Unrecognized flag: %04x\n", (1u << i)); + } + } + return 0; + } + + const auto verb = args[0]; + + if (verb == "set") { + if (args.size() == 2) { + const DWORD newMode = parseInt(args[1]); + setConsoleMode(newMode); + } else if (args.size() == 3) { + const DWORD mode = parseInt(args[1]); + const DWORD mask = parseInt(args[2]); + const int newMode = (getConsoleMode() & ~mask) | (mode & mask); + setConsoleMode(newMode); + } else { + usage(); + } + } else if (verb == "get") { + if (args.size() != 1) { + usage(); + } + printf("0x%lx\n", getConsoleMode()); + } else { + usage(); + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/DebugClient.py b/src/libs/3rdparty/winpty/misc/DebugClient.py new file mode 100644 index 0000000000..cd12df8924 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/DebugClient.py @@ -0,0 +1,42 @@ +#!python +# Run with native CPython. Needs pywin32 extensions. + +# Copyright (c) 2011-2012 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. + +import winerror +import win32pipe +import win32file +import win32api +import sys +import pywintypes +import time + +if len(sys.argv) != 2: + print("Usage: %s message" % sys.argv[0]) + sys.exit(1) + +message = "[%05.3f %s]: %s" % (time.time() % 100000, sys.argv[0], sys.argv[1]) + +win32pipe.CallNamedPipe( + "\\\\.\\pipe\\DebugServer", + message.encode(), + 16, + win32pipe.NMPWAIT_WAIT_FOREVER) diff --git a/src/libs/3rdparty/winpty/misc/DebugServer.py b/src/libs/3rdparty/winpty/misc/DebugServer.py new file mode 100644 index 0000000000..3fc068bae7 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/DebugServer.py @@ -0,0 +1,63 @@ +#!python +# +# Run with native CPython. Needs pywin32 extensions. + +# Copyright (c) 2011-2012 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. + +import win32pipe +import win32api +import win32file +import time +import threading +import sys + +# A message may not be larger than this size. +MSG_SIZE=4096 + +serverPipe = win32pipe.CreateNamedPipe( + "\\\\.\\pipe\\DebugServer", + win32pipe.PIPE_ACCESS_DUPLEX, + win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE, + win32pipe.PIPE_UNLIMITED_INSTANCES, + MSG_SIZE, + MSG_SIZE, + 10 * 1000, + None) +while True: + win32pipe.ConnectNamedPipe(serverPipe, None) + (ret, data) = win32file.ReadFile(serverPipe, MSG_SIZE) + print(data.decode()) + sys.stdout.flush() + + # The client uses CallNamedPipe to send its message. CallNamedPipe waits + # for a reply message. If I send a reply, however, using WriteFile, then + # sometimes WriteFile fails with: + # pywintypes.error: (232, 'WriteFile', 'The pipe is being closed.') + # I can't figure out how to write a strictly correct pipe server, but if + # I comment out the WriteFile line, then everything seems to work. I + # think the DisconnectNamedPipe call aborts the client's CallNamedPipe + # call normally. + + try: + win32file.WriteFile(serverPipe, b'OK') + except: + pass + win32pipe.DisconnectNamedPipe(serverPipe) diff --git a/src/libs/3rdparty/winpty/misc/DumpLines.py b/src/libs/3rdparty/winpty/misc/DumpLines.py new file mode 100644 index 0000000000..40049961b5 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/DumpLines.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +import sys + +for i in range(1, int(sys.argv[1]) + 1): + print i, "X" * 78 diff --git a/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt b/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt new file mode 100644 index 0000000000..37914dac26 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt @@ -0,0 +1,46 @@ +Note regarding ENABLE_EXTENDED_FLAGS (2016-05-30) + +There is a complicated interaction between the ENABLE_EXTENDED_FLAGS flag +and the ENABLE_QUICK_EDIT_MODE and ENABLE_INSERT_MODE flags (presumably for +backwards compatibility?). I studied the behavior on Windows 7 and Windows +10, with both the old and new consoles, and I didn't see any differences +between versions. Here's what I seemed to observe: + + - The console has three flags internally: + - QuickEdit + - InsertMode + - ExtendedFlags + + - SetConsoleMode psuedocode: + void SetConsoleMode(..., DWORD mode) { + ExtendedFlags = (mode & (ENABLE_EXTENDED_FLAGS + | ENABLE_QUICK_EDIT_MODE + | ENABLE_INSERT_MODE )) != 0; + if (ExtendedFlags) { + QuickEdit = (mode & ENABLE_QUICK_EDIT_MODE) != 0; + InsertMode = (mode & ENABLE_INSERT_MODE) != 0; + } + } + + - Setting QuickEdit or InsertMode from the properties dialog GUI does not + affect the ExtendedFlags setting -- it simply toggles the one flag. + + - GetConsoleMode psuedocode: + GetConsoleMode(..., DWORD *result) { + if (ExtendedFlags) { + *result |= ENABLE_EXTENDED_FLAGS; + if (QuickEdit) { *result |= ENABLE_QUICK_EDIT_MODE; } + if (InsertMode) { *result |= ENABLE_INSERT_MODE; } + } + } + +Effectively, the ExtendedFlags flags controls whether the other two flags +are visible/controlled by the user application. If they aren't visible, +though, there is no way for the user application to make them visible, +except by overwriting their values! Calling SetConsoleMode with just +ENABLE_EXTENDED_FLAGS would clear the extended flags we want to read. + +Consequently, if a program temporarily alters the QuickEdit flag (e.g. to +enable mouse input), it cannot restore the original values of the QuickEdit +and InsertMode flags, UNLESS every other console program cooperates by +keeping the ExtendedFlags flag set. diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt new file mode 100644 index 0000000000..067bd3824a --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt @@ -0,0 +1,528 @@ +================================== +Code Page 437, Consolas font +================================== + +Options: -face "Consolas" -family 0x36 +Chars: A2 A3 2014 3044 30FC 4000 + +FontSurvey "-face \"Consolas\" -family 0x36" + +Windows 7 +--------- + +Size 1: 1,3 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 1,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 2,5 BAD (HHHHHH) +Size 6: 3,6 BAD (HHHHHH) +Size 7: 3,6 BAD (HHHHHH) +Size 8: 4,8 BAD (HHHHHH) +Size 9: 4,9 BAD (HHHHHH) +Size 10: 5,10 BAD (HHHHHH) +Size 11: 5,11 BAD (HHHHHH) +Size 12: 6,12 BAD (HHHHHH) +Size 13: 6,13 BAD (HHHHHH) +Size 14: 7,14 BAD (HHHHHH) +Size 15: 7,15 BAD (HHHHHH) +Size 16: 8,16 BAD (HHHHHH) +Size 17: 8,17 BAD (HHHHHH) +Size 18: 8,18 BAD (HHHHHH) +Size 19: 9,19 BAD (HHHHHH) +Size 20: 9,20 BAD (HHHHHH) +Size 21: 10,22 BAD (HHHHHH) +Size 22: 10,22 BAD (HHHHHH) +Size 23: 11,23 BAD (HHHHHH) +Size 24: 11,24 BAD (HHHHHH) +Size 25: 12,25 BAD (HHHHHH) +Size 26: 12,26 BAD (HHHHHH) +Size 27: 13,27 BAD (HHHHHH) +Size 28: 13,28 BAD (HHHHHH) +Size 29: 14,29 BAD (HHHHHH) +Size 30: 14,30 BAD (HHHHHH) +Size 31: 15,31 BAD (HHHHHH) +Size 32: 15,32 BAD (HHHHHH) +Size 33: 15,33 BAD (HHHHHH) +Size 34: 16,34 BAD (HHHHHH) +Size 35: 16,36 BAD (HHHHHH) +Size 36: 17,36 BAD (HHHHHH) +Size 37: 17,37 BAD (HHHHHH) +Size 38: 18,38 BAD (HHHHHH) +Size 39: 18,39 BAD (HHHHHH) +Size 40: 19,40 BAD (HHHHHH) +Size 41: 19,41 BAD (HHHHHH) +Size 42: 20,42 BAD (HHHHHH) +Size 43: 20,43 BAD (HHHHHH) +Size 44: 21,44 BAD (HHHHHH) +Size 45: 21,45 BAD (HHHHHH) +Size 46: 22,46 BAD (HHHHHH) +Size 47: 22,47 BAD (HHHHHH) +Size 48: 23,48 BAD (HHHHHH) +Size 49: 23,49 BAD (HHHHHH) +Size 50: 23,50 BAD (HHHHHH) +Size 51: 24,51 BAD (HHHHHH) +Size 52: 24,52 BAD (HHHHHH) +Size 53: 25,53 BAD (HHHHHH) +Size 54: 25,54 BAD (HHHHHH) +Size 55: 26,55 BAD (HHHHHH) +Size 56: 26,56 BAD (HHHHHH) +Size 57: 27,57 BAD (HHHHHH) +Size 58: 27,58 BAD (HHHHHH) +Size 59: 28,59 BAD (HHHHHH) +Size 60: 28,60 BAD (HHHHHH) +Size 61: 29,61 BAD (HHHHHH) +Size 62: 29,62 BAD (HHHHHH) +Size 63: 30,64 BAD (HHHHHH) +Size 64: 30,64 BAD (HHHHHH) +Size 65: 31,65 BAD (HHHHHH) +Size 66: 31,66 BAD (HHHHHH) +Size 67: 31,67 BAD (HHHHHH) +Size 68: 32,68 BAD (HHHHHH) +Size 69: 32,69 BAD (HHHHHH) +Size 70: 33,70 BAD (HHHHHH) +Size 71: 33,71 BAD (HHHHHH) +Size 72: 34,72 BAD (HHHHHH) +Size 73: 34,73 BAD (HHHHHH) +Size 74: 35,74 BAD (HHHHHH) +Size 75: 35,75 BAD (HHHHHH) +Size 76: 36,76 BAD (HHHHHH) +Size 77: 36,77 BAD (HHHHHH) +Size 78: 37,78 BAD (HHHHHH) +Size 79: 37,79 BAD (HHHHHH) +Size 80: 38,80 BAD (HHHHHH) +Size 81: 38,81 BAD (HHHHHH) +Size 82: 39,82 BAD (HHHHHH) +Size 83: 39,83 BAD (HHHHHH) +Size 84: 39,84 BAD (HHHHHH) +Size 85: 40,85 BAD (HHHHHH) +Size 86: 40,86 BAD (HHHHHH) +Size 87: 41,87 BAD (HHHHHH) +Size 88: 41,88 BAD (HHHHHH) +Size 89: 42,89 BAD (HHHHHH) +Size 90: 42,90 BAD (HHHHHH) +Size 91: 43,91 BAD (HHHHHH) +Size 92: 43,92 BAD (HHHHHH) +Size 93: 44,93 BAD (HHHHHH) +Size 94: 44,94 BAD (HHHHHH) +Size 95: 45,95 BAD (HHHHHH) +Size 96: 45,96 BAD (HHHHHH) +Size 97: 46,97 BAD (HHHHHH) +Size 98: 46,98 BAD (HHHHHH) +Size 99: 46,99 BAD (HHHHHH) +Size 100: 47,100 BAD (HHHHHH) + +Windows 8 +--------- + +Size 1: 1,3 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 1,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 2,5 BAD (HHHHHH) +Size 6: 3,6 BAD (HHHHHH) +Size 7: 3,6 BAD (HHHHHH) +Size 8: 4,8 BAD (HHHHHH) +Size 9: 4,9 BAD (HHHHHH) +Size 10: 5,10 BAD (HHHHHH) +Size 11: 5,11 BAD (HHHHHH) +Size 12: 6,12 BAD (HHHHHH) +Size 13: 6,13 BAD (HHHHHH) +Size 14: 7,14 BAD (HHHHHH) +Size 15: 7,15 BAD (HHHHHH) +Size 16: 8,16 BAD (HHHHHH) +Size 17: 8,17 BAD (HHHHHH) +Size 18: 8,18 BAD (HHHHHH) +Size 19: 9,19 BAD (HHHHHH) +Size 20: 9,20 BAD (HHHHHH) +Size 21: 10,22 BAD (HHHHHH) +Size 22: 10,22 BAD (HHHHHH) +Size 23: 11,23 BAD (HHHHHH) +Size 24: 11,24 BAD (HHHHHH) +Size 25: 12,25 BAD (HHHHHH) +Size 26: 12,26 BAD (HHHHHH) +Size 27: 13,27 BAD (HHHHHH) +Size 28: 13,28 BAD (HHHHHH) +Size 29: 14,29 BAD (HHHHHH) +Size 30: 14,30 BAD (HHHHHH) +Size 31: 15,31 BAD (HHHHHH) +Size 32: 15,32 BAD (HHHHHH) +Size 33: 15,33 BAD (HHHHHH) +Size 34: 16,34 BAD (HHHHHH) +Size 35: 16,36 BAD (HHHHHH) +Size 36: 17,36 BAD (HHHHHH) +Size 37: 17,37 BAD (HHHHHH) +Size 38: 18,38 BAD (HHHHHH) +Size 39: 18,39 BAD (HHHHHH) +Size 40: 19,40 BAD (HHHHHH) +Size 41: 19,41 BAD (HHHHHH) +Size 42: 20,42 BAD (HHHHHH) +Size 43: 20,43 BAD (HHHHHH) +Size 44: 21,44 BAD (HHHHHH) +Size 45: 21,45 BAD (HHHHHH) +Size 46: 22,46 BAD (HHHHHH) +Size 47: 22,47 BAD (HHHHHH) +Size 48: 23,48 BAD (HHHHHH) +Size 49: 23,49 BAD (HHHHHH) +Size 50: 23,50 BAD (HHHHHH) +Size 51: 24,51 BAD (HHHHHH) +Size 52: 24,52 BAD (HHHHHH) +Size 53: 25,53 BAD (HHHHHH) +Size 54: 25,54 BAD (HHHHHH) +Size 55: 26,55 BAD (HHHHHH) +Size 56: 26,56 BAD (HHHHHH) +Size 57: 27,57 BAD (HHHHHH) +Size 58: 27,58 BAD (HHHHHH) +Size 59: 28,59 BAD (HHHHHH) +Size 60: 28,60 BAD (HHHHHH) +Size 61: 29,61 BAD (HHHHHH) +Size 62: 29,62 BAD (HHHHHH) +Size 63: 30,64 BAD (HHHHHH) +Size 64: 30,64 BAD (HHHHHH) +Size 65: 31,65 BAD (HHHHHH) +Size 66: 31,66 BAD (HHHHHH) +Size 67: 31,67 BAD (HHHHHH) +Size 68: 32,68 BAD (HHHHHH) +Size 69: 32,69 BAD (HHHHHH) +Size 70: 33,70 BAD (HHHHHH) +Size 71: 33,71 BAD (HHHHHH) +Size 72: 34,72 BAD (HHHHHH) +Size 73: 34,73 BAD (HHHHHH) +Size 74: 35,74 BAD (HHHHHH) +Size 75: 35,75 BAD (HHHHHH) +Size 76: 36,76 BAD (HHHHHH) +Size 77: 36,77 BAD (HHHHHH) +Size 78: 37,78 BAD (HHHHHH) +Size 79: 37,79 BAD (HHHHHH) +Size 80: 38,80 BAD (HHHHHH) +Size 81: 38,81 BAD (HHHHHH) +Size 82: 39,82 BAD (HHHHHH) +Size 83: 39,83 BAD (HHHHHH) +Size 84: 39,84 BAD (HHHHHH) +Size 85: 40,85 BAD (HHHHHH) +Size 86: 40,86 BAD (HHHHHH) +Size 87: 41,87 BAD (HHHHHH) +Size 88: 41,88 BAD (HHHHHH) +Size 89: 42,89 BAD (HHHHHH) +Size 90: 42,90 BAD (HHHHHH) +Size 91: 43,91 BAD (HHHHHH) +Size 92: 43,92 BAD (HHHHHH) +Size 93: 44,93 BAD (HHHHHH) +Size 94: 44,94 BAD (HHHHHH) +Size 95: 45,95 BAD (HHHHHH) +Size 96: 45,96 BAD (HHHHHH) +Size 97: 46,97 BAD (HHHHHH) +Size 98: 46,98 BAD (HHHHHH) +Size 99: 46,99 BAD (HHHHHH) +Size 100: 47,100 BAD (HHHHHH) + +Windows 8.1 +----------- + +Size 1: 1,3 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 1,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 2,5 BAD (HHHHHH) +Size 6: 3,6 BAD (HHHHHH) +Size 7: 3,6 BAD (HHHHHH) +Size 8: 4,8 BAD (HHHHHH) +Size 9: 4,9 BAD (HHHHHH) +Size 10: 5,10 BAD (HHHHHH) +Size 11: 5,11 BAD (HHHHHH) +Size 12: 6,12 BAD (HHHHHH) +Size 13: 6,13 BAD (HHHHHH) +Size 14: 7,14 BAD (HHHHHH) +Size 15: 7,15 BAD (HHHHHH) +Size 16: 8,16 BAD (HHHHHH) +Size 17: 8,17 BAD (HHHHHH) +Size 18: 8,18 BAD (HHHHHH) +Size 19: 9,19 BAD (HHHHHH) +Size 20: 9,20 BAD (HHHHHH) +Size 21: 10,22 BAD (HHHHHH) +Size 22: 10,22 BAD (HHHHHH) +Size 23: 11,23 BAD (HHHHHH) +Size 24: 11,24 BAD (HHHHHH) +Size 25: 12,25 BAD (HHHHHH) +Size 26: 12,26 BAD (HHHHHH) +Size 27: 13,27 BAD (HHHHHH) +Size 28: 13,28 BAD (HHHHHH) +Size 29: 14,29 BAD (HHHHHH) +Size 30: 14,30 BAD (HHHHHH) +Size 31: 15,31 BAD (HHHHHH) +Size 32: 15,32 BAD (HHHHHH) +Size 33: 15,33 BAD (HHHHHH) +Size 34: 16,34 BAD (HHHHHH) +Size 35: 16,36 BAD (HHHHHH) +Size 36: 17,36 BAD (HHHHHH) +Size 37: 17,37 BAD (HHHHHH) +Size 38: 18,38 BAD (HHHHHH) +Size 39: 18,39 BAD (HHHHHH) +Size 40: 19,40 BAD (HHHHHH) +Size 41: 19,41 BAD (HHHHHH) +Size 42: 20,42 BAD (HHHHHH) +Size 43: 20,43 BAD (HHHHHH) +Size 44: 21,44 BAD (HHHHHH) +Size 45: 21,45 BAD (HHHHHH) +Size 46: 22,46 BAD (HHHHHH) +Size 47: 22,47 BAD (HHHHHH) +Size 48: 23,48 BAD (HHHHHH) +Size 49: 23,49 BAD (HHHHHH) +Size 50: 23,50 BAD (HHHHHH) +Size 51: 24,51 BAD (HHHHHH) +Size 52: 24,52 BAD (HHHHHH) +Size 53: 25,53 BAD (HHHHHH) +Size 54: 25,54 BAD (HHHHHH) +Size 55: 26,55 BAD (HHHHHH) +Size 56: 26,56 BAD (HHHHHH) +Size 57: 27,57 BAD (HHHHHH) +Size 58: 27,58 BAD (HHHHHH) +Size 59: 28,59 BAD (HHHHHH) +Size 60: 28,60 BAD (HHHHHH) +Size 61: 29,61 BAD (HHHHHH) +Size 62: 29,62 BAD (HHHHHH) +Size 63: 30,64 BAD (HHHHHH) +Size 64: 30,64 BAD (HHHHHH) +Size 65: 31,65 BAD (HHHHHH) +Size 66: 31,66 BAD (HHHHHH) +Size 67: 31,67 BAD (HHHHHH) +Size 68: 32,68 BAD (HHHHHH) +Size 69: 32,69 BAD (HHHHHH) +Size 70: 33,70 BAD (HHHHHH) +Size 71: 33,71 BAD (HHHHHH) +Size 72: 34,72 BAD (HHHHHH) +Size 73: 34,73 BAD (HHHHHH) +Size 74: 35,74 BAD (HHHHHH) +Size 75: 35,75 BAD (HHHHHH) +Size 76: 36,76 BAD (HHHHHH) +Size 77: 36,77 BAD (HHHHHH) +Size 78: 37,78 BAD (HHHHHH) +Size 79: 37,79 BAD (HHHHHH) +Size 80: 38,80 BAD (HHHHHH) +Size 81: 38,81 BAD (HHHHHH) +Size 82: 39,82 BAD (HHHHHH) +Size 83: 39,83 BAD (HHHHHH) +Size 84: 39,84 BAD (HHHHHH) +Size 85: 40,85 BAD (HHHHHH) +Size 86: 40,86 BAD (HHHHHH) +Size 87: 41,87 BAD (HHHHHH) +Size 88: 41,88 BAD (HHHHHH) +Size 89: 42,89 BAD (HHHHHH) +Size 90: 42,90 BAD (HHHHHH) +Size 91: 43,91 BAD (HHHHHH) +Size 92: 43,92 BAD (HHHHHH) +Size 93: 44,93 BAD (HHHHHH) +Size 94: 44,94 BAD (HHHHHH) +Size 95: 45,95 BAD (HHHHHH) +Size 96: 45,96 BAD (HHHHHH) +Size 97: 46,97 BAD (HHHHHH) +Size 98: 46,98 BAD (HHHHHH) +Size 99: 46,99 BAD (HHHHHH) +Size 100: 47,100 BAD (HHHHHH) + +Windows 10 14342 Old Console +---------------------------- + +Size 1: 1,3 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 1,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 2,5 BAD (HHHHHH) +Size 6: 3,6 BAD (HHHHHH) +Size 7: 3,6 BAD (HHHHHH) +Size 8: 4,8 BAD (HHHHHH) +Size 9: 4,9 BAD (HHHHHH) +Size 10: 5,10 BAD (HHHHHH) +Size 11: 5,11 BAD (HHHHHH) +Size 12: 6,12 BAD (HHHHHH) +Size 13: 6,13 BAD (HHHHHH) +Size 14: 7,14 BAD (HHHHHH) +Size 15: 7,15 BAD (HHHHHH) +Size 16: 8,16 BAD (HHHHHH) +Size 17: 8,17 BAD (HHHHHH) +Size 18: 8,18 BAD (HHHHHH) +Size 19: 9,19 BAD (HHHHHH) +Size 20: 9,20 BAD (HHHHHH) +Size 21: 10,22 BAD (HHHHHH) +Size 22: 10,22 BAD (HHHHHH) +Size 23: 11,23 BAD (HHHHHH) +Size 24: 11,24 BAD (HHHHHH) +Size 25: 12,25 BAD (HHHHHH) +Size 26: 12,26 BAD (HHHHHH) +Size 27: 13,27 BAD (HHHHHH) +Size 28: 13,28 BAD (HHHHHH) +Size 29: 14,29 BAD (HHHHHH) +Size 30: 14,30 BAD (HHHHHH) +Size 31: 15,31 BAD (HHHHHH) +Size 32: 15,32 BAD (HHHHHH) +Size 33: 15,33 BAD (HHHHHH) +Size 34: 16,34 BAD (HHHHHH) +Size 35: 16,36 BAD (HHHHHH) +Size 36: 17,36 BAD (HHHHHH) +Size 37: 17,37 BAD (HHHHHH) +Size 38: 18,38 BAD (HHHHHH) +Size 39: 18,39 BAD (HHHHHH) +Size 40: 19,40 BAD (HHHHHH) +Size 41: 19,41 BAD (HHHHHH) +Size 42: 20,42 BAD (HHHHHH) +Size 43: 20,43 BAD (HHHHHH) +Size 44: 21,44 BAD (HHHHHH) +Size 45: 21,45 BAD (HHHHHH) +Size 46: 22,46 BAD (HHHHHH) +Size 47: 22,47 BAD (HHHHHH) +Size 48: 23,48 BAD (HHHHHH) +Size 49: 23,49 BAD (HHHHHH) +Size 50: 23,50 BAD (HHHHHH) +Size 51: 24,51 BAD (HHHHHH) +Size 52: 24,52 BAD (HHHHHH) +Size 53: 25,53 BAD (HHHHHH) +Size 54: 25,54 BAD (HHHHHH) +Size 55: 26,55 BAD (HHHHHH) +Size 56: 26,56 BAD (HHHHHH) +Size 57: 27,57 BAD (HHHHHH) +Size 58: 27,58 BAD (HHHHHH) +Size 59: 28,59 BAD (HHHHHH) +Size 60: 28,60 BAD (HHHHHH) +Size 61: 29,61 BAD (HHHHHH) +Size 62: 29,62 BAD (HHHHHH) +Size 63: 30,64 BAD (HHHHHH) +Size 64: 30,64 BAD (HHHHHH) +Size 65: 31,65 BAD (HHHHHH) +Size 66: 31,66 BAD (HHHHHH) +Size 67: 31,67 BAD (HHHHHH) +Size 68: 32,68 BAD (HHHHHH) +Size 69: 32,69 BAD (HHHHHH) +Size 70: 33,70 BAD (HHHHHH) +Size 71: 33,71 BAD (HHHHHH) +Size 72: 34,72 BAD (HHHHHH) +Size 73: 34,73 BAD (HHHHHH) +Size 74: 35,74 BAD (HHHHHH) +Size 75: 35,75 BAD (HHHHHH) +Size 76: 36,76 BAD (HHHHHH) +Size 77: 36,77 BAD (HHHHHH) +Size 78: 37,78 BAD (HHHHHH) +Size 79: 37,79 BAD (HHHHHH) +Size 80: 38,80 BAD (HHHHHH) +Size 81: 38,81 BAD (HHHHHH) +Size 82: 39,82 BAD (HHHHHH) +Size 83: 39,83 BAD (HHHHHH) +Size 84: 39,84 BAD (HHHHHH) +Size 85: 40,85 BAD (HHHHHH) +Size 86: 40,86 BAD (HHHHHH) +Size 87: 41,87 BAD (HHHHHH) +Size 88: 41,88 BAD (HHHHHH) +Size 89: 42,89 BAD (HHHHHH) +Size 90: 42,90 BAD (HHHHHH) +Size 91: 43,91 BAD (HHHHHH) +Size 92: 43,92 BAD (HHHHHH) +Size 93: 44,93 BAD (HHHHHH) +Size 94: 44,94 BAD (HHHHHH) +Size 95: 45,95 BAD (HHHHHH) +Size 96: 45,96 BAD (HHHHHH) +Size 97: 46,97 BAD (HHHHHH) +Size 98: 46,98 BAD (HHHHHH) +Size 99: 46,99 BAD (HHHHHH) +Size 100: 47,100 BAD (HHHHHH) + +Windows 10 14342 New Console +---------------------------- + +Size 1: 1,1 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 1,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 2,5 BAD (HHHHHH) +Size 6: 3,6 BAD (HHHHHH) +Size 7: 3,7 BAD (HHHHHH) +Size 8: 4,8 BAD (HHHHHH) +Size 9: 4,9 BAD (HHHHHH) +Size 10: 5,10 BAD (HHHHHH) +Size 11: 5,11 BAD (HHHHHH) +Size 12: 6,12 BAD (HHHHHH) +Size 13: 6,13 BAD (HHHHHH) +Size 14: 7,14 BAD (HHHHHH) +Size 15: 7,15 BAD (HHHHHH) +Size 16: 8,16 BAD (HHHHHH) +Size 17: 8,17 BAD (HHHHHH) +Size 18: 8,18 BAD (HHHHHH) +Size 19: 9,19 BAD (HHHHHH) +Size 20: 9,20 BAD (HHHHHH) +Size 21: 10,21 BAD (HHHHHH) +Size 22: 10,22 BAD (HHHHHH) +Size 23: 11,23 BAD (HHHHHH) +Size 24: 11,24 BAD (HHHHHH) +Size 25: 12,25 BAD (HHHHHH) +Size 26: 12,26 BAD (HHHHHH) +Size 27: 13,27 BAD (HHHHHH) +Size 28: 13,28 BAD (HHHHHH) +Size 29: 14,29 BAD (HHHHHH) +Size 30: 14,30 BAD (HHHHHH) +Size 31: 15,31 BAD (HHHHHH) +Size 32: 15,32 BAD (HHHHHH) +Size 33: 15,33 BAD (HHHHHH) +Size 34: 16,34 BAD (HHHHHH) +Size 35: 16,35 BAD (HHHHHH) +Size 36: 17,36 BAD (HHHHHH) +Size 37: 17,37 BAD (HHHHHH) +Size 38: 18,38 BAD (HHHHHH) +Size 39: 18,39 BAD (HHHHHH) +Size 40: 19,40 BAD (HHHHHH) +Size 41: 19,41 BAD (HHHHHH) +Size 42: 20,42 BAD (HHHHHH) +Size 43: 20,43 BAD (HHHHHH) +Size 44: 21,44 BAD (HHHHHH) +Size 45: 21,45 BAD (HHHHHH) +Size 46: 22,46 BAD (HHHHHH) +Size 47: 22,47 BAD (HHHHHH) +Size 48: 23,48 BAD (HHHHHH) +Size 49: 23,49 BAD (HHHHHH) +Size 50: 23,50 BAD (HHHHHH) +Size 51: 24,51 BAD (HHHHHH) +Size 52: 24,52 BAD (HHHHHH) +Size 53: 25,53 BAD (HHHHHH) +Size 54: 25,54 BAD (HHHHHH) +Size 55: 26,55 BAD (HHHHHH) +Size 56: 26,56 BAD (HHHHHH) +Size 57: 27,57 BAD (HHHHHH) +Size 58: 27,58 BAD (HHHHHH) +Size 59: 28,59 BAD (HHHHHH) +Size 60: 28,60 BAD (HHHHHH) +Size 61: 29,61 BAD (HHHHHH) +Size 62: 29,62 BAD (HHHHHH) +Size 63: 30,63 BAD (HHHHHH) +Size 64: 30,64 BAD (HHHHHH) +Size 65: 31,65 BAD (HHHHHH) +Size 66: 31,66 BAD (HHHHHH) +Size 67: 31,67 BAD (HHHHHH) +Size 68: 32,68 BAD (HHHHHH) +Size 69: 32,69 BAD (HHHHHH) +Size 70: 33,70 BAD (HHHHHH) +Size 71: 33,71 BAD (HHHHHH) +Size 72: 34,72 BAD (HHHHHH) +Size 73: 34,73 BAD (HHHHHH) +Size 74: 35,74 BAD (HHHHHH) +Size 75: 35,75 BAD (HHHHHH) +Size 76: 36,76 BAD (HHHHHH) +Size 77: 36,77 BAD (HHHHHH) +Size 78: 37,78 BAD (HHHHHH) +Size 79: 37,79 BAD (HHHHHH) +Size 80: 38,80 BAD (HHHHHH) +Size 81: 38,81 BAD (HHHHHH) +Size 82: 39,82 BAD (HHHHHH) +Size 83: 39,83 BAD (HHHHHH) +Size 84: 39,84 BAD (HHHHHH) +Size 85: 40,85 BAD (HHHHHH) +Size 86: 40,86 BAD (HHHHHH) +Size 87: 41,87 BAD (HHHHHH) +Size 88: 41,88 BAD (HHHHHH) +Size 89: 42,89 BAD (HHHHHH) +Size 90: 42,90 BAD (HHHHHH) +Size 91: 43,91 BAD (HHHHHH) +Size 92: 43,92 BAD (HHHHHH) +Size 93: 44,93 BAD (HHHHHH) +Size 94: 44,94 BAD (HHHHHH) +Size 95: 45,95 BAD (HHHHHH) +Size 96: 45,96 BAD (HHHHHH) +Size 97: 46,97 BAD (HHHHHH) +Size 98: 46,98 BAD (HHHHHH) +Size 99: 46,99 BAD (HHHHHH) +Size 100: 47,100 BAD (HHHHHH) diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt new file mode 100644 index 0000000000..0eed93ad98 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt @@ -0,0 +1,633 @@ +================================== +Code Page 437, Lucida Console font +================================== + +Options: -face "Lucida Console" -family 0x36 +Chars: A2 A3 2014 3044 30FC 4000 + +FontSurvey "-face \"Lucida Console\" -family 0x36" + +Vista +----- + +Size 1: 1,2 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 2,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 3,5 BAD (HHHHHH) +Size 6: 4,6 BAD (HHHHHH) +Size 7: 4,7 BAD (HHHHHH) +Size 8: 5,8 BAD (HHHHHH) +Size 9: 5,9 BAD (HHHHHH) +Size 10: 6,10 BAD (HHHHHH) +Size 11: 7,11 BAD (HHHHHH) +Size 12: 7,12 BAD (HHHHHH) +Size 13: 8,13 BAD (HHHHHH) +Size 14: 8,14 BAD (HHHHHH) +Size 15: 9,15 BAD (HHHHHH) +Size 16: 10,16 BAD (HHHHHH) +Size 17: 10,17 BAD (HHHHHH) +Size 18: 11,18 BAD (HHHHHH) +Size 19: 11,19 BAD (HHHHHH) +Size 20: 12,20 BAD (HHHHHH) +Size 21: 13,21 BAD (HHHHHH) +Size 22: 13,22 BAD (HHHHHH) +Size 23: 14,23 BAD (HHHHHH) +Size 24: 14,24 BAD (HHHHHH) +Size 25: 15,25 BAD (HHHHHH) +Size 26: 16,26 BAD (HHHHHH) +Size 27: 16,27 BAD (HHHHHH) +Size 28: 17,28 BAD (HHHHHH) +Size 29: 17,29 BAD (HHHHHH) +Size 30: 18,30 BAD (HHHHHH) +Size 31: 19,31 BAD (HHHHHH) +Size 32: 19,32 BAD (HHHHHH) +Size 33: 20,33 BAD (HHHHHH) +Size 34: 20,34 BAD (HHHHHH) +Size 35: 21,35 BAD (HHHHHH) +Size 36: 22,36 BAD (HHHHHH) +Size 37: 22,37 BAD (HHHHHH) +Size 38: 23,38 BAD (HHHHHH) +Size 39: 23,39 BAD (HHHHHH) +Size 40: 24,40 BAD (HHHHHH) +Size 41: 25,41 BAD (HHHHHH) +Size 42: 25,42 BAD (HHHHHH) +Size 43: 26,43 BAD (HHHHHH) +Size 44: 27,44 BAD (HHHHHH) +Size 45: 27,45 BAD (HHHHHH) +Size 46: 28,46 BAD (HHHHHH) +Size 47: 28,47 BAD (HHHHHH) +Size 48: 29,48 BAD (HHHHHH) +Size 49: 30,49 BAD (HHHHHH) +Size 50: 30,50 BAD (HHHHHH) +Size 51: 31,51 BAD (HHHHHH) +Size 52: 31,52 BAD (HHHHHH) +Size 53: 32,53 BAD (HHHHHH) +Size 54: 33,54 BAD (HHHHHH) +Size 55: 33,55 BAD (HHHHHH) +Size 56: 34,56 BAD (HHHHHH) +Size 57: 34,57 BAD (HHHHHH) +Size 58: 35,58 BAD (HHHHHH) +Size 59: 36,59 BAD (HHHHHH) +Size 60: 36,60 BAD (HHHHHH) +Size 61: 37,61 BAD (HHHHHH) +Size 62: 37,62 BAD (HHHHHH) +Size 63: 38,63 BAD (HHHHHH) +Size 64: 39,65 BAD (HHHHHH) +Size 65: 39,65 BAD (HHHHHH) +Size 66: 40,66 BAD (HHHHHH) +Size 67: 40,67 BAD (HHHHHH) +Size 68: 41,68 BAD (HHHHHH) +Size 69: 42,69 BAD (HHHHHH) +Size 70: 42,70 BAD (HHHHHH) +Size 71: 43,71 BAD (HHHHHH) +Size 72: 43,72 BAD (HHHHHH) +Size 73: 44,73 BAD (HHHHHH) +Size 74: 45,74 BAD (HHHHHH) +Size 75: 45,75 BAD (HHHHHH) +Size 76: 46,76 BAD (HHHHHH) +Size 77: 46,77 BAD (HHHHHH) +Size 78: 47,78 BAD (HHHHHH) +Size 79: 48,79 BAD (HHHHHH) +Size 80: 48,80 BAD (HHHHHH) +Size 81: 49,81 BAD (HHHHHH) +Size 82: 49,82 BAD (HHHHHH) +Size 83: 50,83 BAD (HHHHHH) +Size 84: 51,84 BAD (HHHHHH) +Size 85: 51,85 BAD (HHHHHH) +Size 86: 52,86 BAD (HHHHHH) +Size 87: 52,87 BAD (HHHHHH) +Size 88: 53,88 BAD (HHHHHH) +Size 89: 54,89 BAD (HHHHHH) +Size 90: 54,90 BAD (HHHHHH) +Size 91: 55,91 BAD (HHHHHH) +Size 92: 55,92 BAD (HHHHHH) +Size 93: 56,93 BAD (HHHHHH) +Size 94: 57,94 BAD (HHHHHH) +Size 95: 57,95 BAD (HHHHHH) +Size 96: 58,96 BAD (HHHHHH) +Size 97: 58,97 BAD (HHHHHH) +Size 98: 59,98 BAD (HHHHHH) +Size 99: 60,99 BAD (HHHHHH) +Size 100: 60,100 BAD (HHHHHH) + + +Windows 7 +--------- + +Size 1: 1,2 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 2,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 3,5 BAD (HHHHHH) +Size 6: 4,6 BAD (HHHHHH) +Size 7: 4,7 BAD (HHHHHH) +Size 8: 5,8 BAD (HHHHHH) +Size 9: 5,9 BAD (HHHHHH) +Size 10: 6,10 BAD (HHHHHH) +Size 11: 7,11 BAD (HHHHHH) +Size 12: 7,12 BAD (HHHHHH) +Size 13: 8,13 BAD (HHHHHH) +Size 14: 8,14 BAD (HHHHHH) +Size 15: 9,15 BAD (HHHHHH) +Size 16: 10,16 BAD (HHHHHH) +Size 17: 10,17 BAD (HHHHHH) +Size 18: 11,18 BAD (HHHHHH) +Size 19: 11,19 BAD (HHHHHH) +Size 20: 12,20 BAD (HHHHHH) +Size 21: 13,21 BAD (HHHHHH) +Size 22: 13,22 BAD (HHHHHH) +Size 23: 14,23 BAD (HHHHHH) +Size 24: 14,24 BAD (HHHHHH) +Size 25: 15,25 BAD (HHHHHH) +Size 26: 16,26 BAD (HHHHHH) +Size 27: 16,27 BAD (HHHHHH) +Size 28: 17,28 BAD (HHHHHH) +Size 29: 17,29 BAD (HHHHHH) +Size 30: 18,30 BAD (HHHHHH) +Size 31: 19,31 BAD (HHHHHH) +Size 32: 19,32 BAD (HHHHHH) +Size 33: 20,33 BAD (HHHHHH) +Size 34: 20,34 BAD (HHHHHH) +Size 35: 21,35 BAD (HHHHHH) +Size 36: 22,36 BAD (HHHHHH) +Size 37: 22,37 BAD (HHHHHH) +Size 38: 23,38 BAD (HHHHHH) +Size 39: 23,39 BAD (HHHHHH) +Size 40: 24,40 BAD (HHHHHH) +Size 41: 25,41 BAD (HHHHHH) +Size 42: 25,42 BAD (HHHHHH) +Size 43: 26,43 BAD (HHHHHH) +Size 44: 27,44 BAD (HHHHHH) +Size 45: 27,45 BAD (HHHHHH) +Size 46: 28,46 BAD (HHHHHH) +Size 47: 28,47 BAD (HHHHHH) +Size 48: 29,48 BAD (HHHHHH) +Size 49: 30,49 BAD (HHHHHH) +Size 50: 30,50 BAD (HHHHHH) +Size 51: 31,51 BAD (HHHHHH) +Size 52: 31,52 BAD (HHHHHH) +Size 53: 32,53 BAD (HHHHHH) +Size 54: 33,54 BAD (HHHHHH) +Size 55: 33,55 BAD (HHHHHH) +Size 56: 34,56 BAD (HHHHHH) +Size 57: 34,57 BAD (HHHHHH) +Size 58: 35,58 BAD (HHHHHH) +Size 59: 36,59 BAD (HHHHHH) +Size 60: 36,60 BAD (HHHHHH) +Size 61: 37,61 BAD (HHHHHH) +Size 62: 37,62 BAD (HHHHHH) +Size 63: 38,63 BAD (HHHHHH) +Size 64: 39,65 BAD (HHHHHH) +Size 65: 39,65 BAD (HHHHHH) +Size 66: 40,66 BAD (HHHHHH) +Size 67: 40,67 BAD (HHHHHH) +Size 68: 41,68 BAD (HHHHHH) +Size 69: 42,69 BAD (HHHHHH) +Size 70: 42,70 BAD (HHHHHH) +Size 71: 43,71 BAD (HHHHHH) +Size 72: 43,72 BAD (HHHHHH) +Size 73: 44,73 BAD (HHHHHH) +Size 74: 45,74 BAD (HHHHHH) +Size 75: 45,75 BAD (HHHHHH) +Size 76: 46,76 BAD (HHHHHH) +Size 77: 46,77 BAD (HHHHHH) +Size 78: 47,78 BAD (HHHHHH) +Size 79: 48,79 BAD (HHHHHH) +Size 80: 48,80 BAD (HHHHHH) +Size 81: 49,81 BAD (HHHHHH) +Size 82: 49,82 BAD (HHHHHH) +Size 83: 50,83 BAD (HHHHHH) +Size 84: 51,84 BAD (HHHHHH) +Size 85: 51,85 BAD (HHHHHH) +Size 86: 52,86 BAD (HHHHHH) +Size 87: 52,87 BAD (HHHHHH) +Size 88: 53,88 BAD (HHHHHH) +Size 89: 54,89 BAD (HHHHHH) +Size 90: 54,90 BAD (HHHHHH) +Size 91: 55,91 BAD (HHHHHH) +Size 92: 55,92 BAD (HHHHHH) +Size 93: 56,93 BAD (HHHHHH) +Size 94: 57,94 BAD (HHHHHH) +Size 95: 57,95 BAD (HHHHHH) +Size 96: 58,96 BAD (HHHHHH) +Size 97: 58,97 BAD (HHHHHH) +Size 98: 59,98 BAD (HHHHHH) +Size 99: 60,99 BAD (HHHHHH) +Size 100: 60,100 BAD (HHHHHH) + +Windows 8 +--------- + +Size 1: 1,2 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 2,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 3,5 BAD (HHHHHH) +Size 6: 4,6 BAD (HHHHHH) +Size 7: 4,7 BAD (HHHHHH) +Size 8: 5,8 BAD (HHHHHH) +Size 9: 5,9 BAD (HHHHHH) +Size 10: 6,10 BAD (HHHHHH) +Size 11: 7,11 BAD (HHHHHH) +Size 12: 7,12 BAD (HHHHHH) +Size 13: 8,13 BAD (HHHHHH) +Size 14: 8,14 BAD (HHHHHH) +Size 15: 9,15 BAD (HHHHHH) +Size 16: 10,16 BAD (HHHHHH) +Size 17: 10,17 BAD (HHHHHH) +Size 18: 11,18 BAD (HHHHHH) +Size 19: 11,19 BAD (HHHHHH) +Size 20: 12,20 BAD (HHHHHH) +Size 21: 13,21 BAD (HHHHHH) +Size 22: 13,22 BAD (HHHHHH) +Size 23: 14,23 BAD (HHHHHH) +Size 24: 14,24 BAD (HHHHHH) +Size 25: 15,25 BAD (HHHHHH) +Size 26: 16,26 BAD (HHHHHH) +Size 27: 16,27 BAD (HHHHHH) +Size 28: 17,28 BAD (HHHHHH) +Size 29: 17,29 BAD (HHHHHH) +Size 30: 18,30 BAD (HHHHHH) +Size 31: 19,31 BAD (HHHHHH) +Size 32: 19,32 BAD (HHHHHH) +Size 33: 20,33 BAD (HHHHHH) +Size 34: 20,34 BAD (HHHHHH) +Size 35: 21,35 BAD (HHHHHH) +Size 36: 22,36 BAD (HHHHHH) +Size 37: 22,37 BAD (HHHHHH) +Size 38: 23,38 BAD (HHHHHH) +Size 39: 23,39 BAD (HHHHHH) +Size 40: 24,40 BAD (HHHHHH) +Size 41: 25,41 BAD (HHHHHH) +Size 42: 25,42 BAD (HHHHHH) +Size 43: 26,43 BAD (HHHHHH) +Size 44: 27,44 BAD (HHHHHH) +Size 45: 27,45 BAD (HHHHHH) +Size 46: 28,46 BAD (HHHHHH) +Size 47: 28,47 BAD (HHHHHH) +Size 48: 29,48 BAD (HHHHHH) +Size 49: 30,49 BAD (HHHHHH) +Size 50: 30,50 BAD (HHHHHH) +Size 51: 31,51 BAD (HHHHHH) +Size 52: 31,52 BAD (HHHHHH) +Size 53: 32,53 BAD (HHHHHH) +Size 54: 33,54 BAD (HHHHHH) +Size 55: 33,55 BAD (HHHHHH) +Size 56: 34,56 BAD (HHHHHH) +Size 57: 34,57 BAD (HHHHHH) +Size 58: 35,58 BAD (HHHHHH) +Size 59: 36,59 BAD (HHHHHH) +Size 60: 36,60 BAD (HHHHHH) +Size 61: 37,61 BAD (HHHHHH) +Size 62: 37,62 BAD (HHHHHH) +Size 63: 38,63 BAD (HHHHHH) +Size 64: 39,65 BAD (HHHHHH) +Size 65: 39,65 BAD (HHHHHH) +Size 66: 40,66 BAD (HHHHHH) +Size 67: 40,67 BAD (HHHHHH) +Size 68: 41,68 BAD (HHHHHH) +Size 69: 42,69 BAD (HHHHHH) +Size 70: 42,70 BAD (HHHHHH) +Size 71: 43,71 BAD (HHHHHH) +Size 72: 43,72 BAD (HHHHHH) +Size 73: 44,73 BAD (HHHHHH) +Size 74: 45,74 BAD (HHHHHH) +Size 75: 45,75 BAD (HHHHHH) +Size 76: 46,76 BAD (HHHHHH) +Size 77: 46,77 BAD (HHHHHH) +Size 78: 47,78 BAD (HHHHHH) +Size 79: 48,79 BAD (HHHHHH) +Size 80: 48,80 BAD (HHHHHH) +Size 81: 49,81 BAD (HHHHHH) +Size 82: 49,82 BAD (HHHHHH) +Size 83: 50,83 BAD (HHHHHH) +Size 84: 51,84 BAD (HHHHHH) +Size 85: 51,85 BAD (HHHHHH) +Size 86: 52,86 BAD (HHHHHH) +Size 87: 52,87 BAD (HHHHHH) +Size 88: 53,88 BAD (HHHHHH) +Size 89: 54,89 BAD (HHHHHH) +Size 90: 54,90 BAD (HHHHHH) +Size 91: 55,91 BAD (HHHHHH) +Size 92: 55,92 BAD (HHHHHH) +Size 93: 56,93 BAD (HHHHHH) +Size 94: 57,94 BAD (HHHHHH) +Size 95: 57,95 BAD (HHHHHH) +Size 96: 58,96 BAD (HHHHHH) +Size 97: 58,97 BAD (HHHHHH) +Size 98: 59,98 BAD (HHHHHH) +Size 99: 60,99 BAD (HHHHHH) +Size 100: 60,100 BAD (HHHHHH) + +Windows 8.1 +----------- + +Size 1: 1,2 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 2,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 3,5 BAD (HHHHHH) +Size 6: 4,6 BAD (HHHHHH) +Size 7: 4,7 BAD (HHHHHH) +Size 8: 5,8 BAD (HHHHHH) +Size 9: 5,9 BAD (HHHHHH) +Size 10: 6,10 BAD (HHHHHH) +Size 11: 7,11 BAD (HHHHHH) +Size 12: 7,12 BAD (HHHHHH) +Size 13: 8,13 BAD (HHHHHH) +Size 14: 8,14 BAD (HHHHHH) +Size 15: 9,15 BAD (HHHHHH) +Size 16: 10,16 BAD (HHHHHH) +Size 17: 10,17 BAD (HHHHHH) +Size 18: 11,18 BAD (HHHHHH) +Size 19: 11,19 BAD (HHHHHH) +Size 20: 12,20 BAD (HHHHHH) +Size 21: 13,21 BAD (HHHHHH) +Size 22: 13,22 BAD (HHHHHH) +Size 23: 14,23 BAD (HHHHHH) +Size 24: 14,24 BAD (HHHHHH) +Size 25: 15,25 BAD (HHHHHH) +Size 26: 16,26 BAD (HHHHHH) +Size 27: 16,27 BAD (HHHHHH) +Size 28: 17,28 BAD (HHHHHH) +Size 29: 17,29 BAD (HHHHHH) +Size 30: 18,30 BAD (HHHHHH) +Size 31: 19,31 BAD (HHHHHH) +Size 32: 19,32 BAD (HHHHHH) +Size 33: 20,33 BAD (HHHHHH) +Size 34: 20,34 BAD (HHHHHH) +Size 35: 21,35 BAD (HHHHHH) +Size 36: 22,36 BAD (HHHHHH) +Size 37: 22,37 BAD (HHHHHH) +Size 38: 23,38 BAD (HHHHHH) +Size 39: 23,39 BAD (HHHHHH) +Size 40: 24,40 BAD (HHHHHH) +Size 41: 25,41 BAD (HHHHHH) +Size 42: 25,42 BAD (HHHHHH) +Size 43: 26,43 BAD (HHHHHH) +Size 44: 27,44 BAD (HHHHHH) +Size 45: 27,45 BAD (HHHHHH) +Size 46: 28,46 BAD (HHHHHH) +Size 47: 28,47 BAD (HHHHHH) +Size 48: 29,48 BAD (HHHHHH) +Size 49: 30,49 BAD (HHHHHH) +Size 50: 30,50 BAD (HHHHHH) +Size 51: 31,51 BAD (HHHHHH) +Size 52: 31,52 BAD (HHHHHH) +Size 53: 32,53 BAD (HHHHHH) +Size 54: 33,54 BAD (HHHHHH) +Size 55: 33,55 BAD (HHHHHH) +Size 56: 34,56 BAD (HHHHHH) +Size 57: 34,57 BAD (HHHHHH) +Size 58: 35,58 BAD (HHHHHH) +Size 59: 36,59 BAD (HHHHHH) +Size 60: 36,60 BAD (HHHHHH) +Size 61: 37,61 BAD (HHHHHH) +Size 62: 37,62 BAD (HHHHHH) +Size 63: 38,63 BAD (HHHHHH) +Size 64: 39,65 BAD (HHHHHH) +Size 65: 39,65 BAD (HHHHHH) +Size 66: 40,66 BAD (HHHHHH) +Size 67: 40,67 BAD (HHHHHH) +Size 68: 41,68 BAD (HHHHHH) +Size 69: 42,69 BAD (HHHHHH) +Size 70: 42,70 BAD (HHHHHH) +Size 71: 43,71 BAD (HHHHHH) +Size 72: 43,72 BAD (HHHHHH) +Size 73: 44,73 BAD (HHHHHH) +Size 74: 45,74 BAD (HHHHHH) +Size 75: 45,75 BAD (HHHHHH) +Size 76: 46,76 BAD (HHHHHH) +Size 77: 46,77 BAD (HHHHHH) +Size 78: 47,78 BAD (HHHHHH) +Size 79: 48,79 BAD (HHHHHH) +Size 80: 48,80 BAD (HHHHHH) +Size 81: 49,81 BAD (HHHHHH) +Size 82: 49,82 BAD (HHHHHH) +Size 83: 50,83 BAD (HHHHHH) +Size 84: 51,84 BAD (HHHHHH) +Size 85: 51,85 BAD (HHHHHH) +Size 86: 52,86 BAD (HHHHHH) +Size 87: 52,87 BAD (HHHHHH) +Size 88: 53,88 BAD (HHHHHH) +Size 89: 54,89 BAD (HHHHHH) +Size 90: 54,90 BAD (HHHHHH) +Size 91: 55,91 BAD (HHHHHH) +Size 92: 55,92 BAD (HHHHHH) +Size 93: 56,93 BAD (HHHHHH) +Size 94: 57,94 BAD (HHHHHH) +Size 95: 57,95 BAD (HHHHHH) +Size 96: 58,96 BAD (HHHHHH) +Size 97: 58,97 BAD (HHHHHH) +Size 98: 59,98 BAD (HHHHHH) +Size 99: 60,99 BAD (HHHHHH) +Size 100: 60,100 BAD (HHHHHH) + +Windows 10 14342 Old Console +---------------------------- + +Size 1: 1,2 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 2,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 3,5 BAD (HHHHHH) +Size 6: 4,6 BAD (HHHHHH) +Size 7: 4,7 BAD (HHHHHH) +Size 8: 5,8 BAD (HHHHHH) +Size 9: 5,9 BAD (HHHHHH) +Size 10: 6,10 BAD (HHHHHH) +Size 11: 7,11 BAD (HHHHHH) +Size 12: 7,12 BAD (HHHHHH) +Size 13: 8,13 BAD (HHHHHH) +Size 14: 8,14 BAD (HHHHHH) +Size 15: 9,15 BAD (HHHHHH) +Size 16: 10,16 BAD (HHHHHH) +Size 17: 10,17 BAD (HHHHHH) +Size 18: 11,18 BAD (HHHHHH) +Size 19: 11,19 BAD (HHHHHH) +Size 20: 12,20 BAD (HHHHHH) +Size 21: 13,21 BAD (HHHHHH) +Size 22: 13,22 BAD (HHHHHH) +Size 23: 14,23 BAD (HHHHHH) +Size 24: 14,24 BAD (HHHHHH) +Size 25: 15,25 BAD (HHHHHH) +Size 26: 16,26 BAD (HHHHHH) +Size 27: 16,27 BAD (HHHHHH) +Size 28: 17,28 BAD (HHHHHH) +Size 29: 17,29 BAD (HHHHHH) +Size 30: 18,30 BAD (HHHHHH) +Size 31: 19,31 BAD (HHHHHH) +Size 32: 19,32 BAD (HHHHHH) +Size 33: 20,33 BAD (HHHHHH) +Size 34: 20,34 BAD (HHHHHH) +Size 35: 21,35 BAD (HHHHHH) +Size 36: 22,36 BAD (HHHHHH) +Size 37: 22,37 BAD (HHHHHH) +Size 38: 23,38 BAD (HHHHHH) +Size 39: 23,39 BAD (HHHHHH) +Size 40: 24,40 BAD (HHHHHH) +Size 41: 25,41 BAD (HHHHHH) +Size 42: 25,42 BAD (HHHHHH) +Size 43: 26,43 BAD (HHHHHH) +Size 44: 27,44 BAD (HHHHHH) +Size 45: 27,45 BAD (HHHHHH) +Size 46: 28,46 BAD (HHHHHH) +Size 47: 28,47 BAD (HHHHHH) +Size 48: 29,48 BAD (HHHHHH) +Size 49: 30,49 BAD (HHHHHH) +Size 50: 30,50 BAD (HHHHHH) +Size 51: 31,51 BAD (HHHHHH) +Size 52: 31,52 BAD (HHHHHH) +Size 53: 32,53 BAD (HHHHHH) +Size 54: 33,54 BAD (HHHHHH) +Size 55: 33,55 BAD (HHHHHH) +Size 56: 34,56 BAD (HHHHHH) +Size 57: 34,57 BAD (HHHHHH) +Size 58: 35,58 BAD (HHHHHH) +Size 59: 36,59 BAD (HHHHHH) +Size 60: 36,60 BAD (HHHHHH) +Size 61: 37,61 BAD (HHHHHH) +Size 62: 37,62 BAD (HHHHHH) +Size 63: 38,63 BAD (HHHHHH) +Size 64: 39,65 BAD (HHHHHH) +Size 65: 39,65 BAD (HHHHHH) +Size 66: 40,66 BAD (HHHHHH) +Size 67: 40,67 BAD (HHHHHH) +Size 68: 41,68 BAD (HHHHHH) +Size 69: 42,69 BAD (HHHHHH) +Size 70: 42,70 BAD (HHHHHH) +Size 71: 43,71 BAD (HHHHHH) +Size 72: 43,72 BAD (HHHHHH) +Size 73: 44,73 BAD (HHHHHH) +Size 74: 45,74 BAD (HHHHHH) +Size 75: 45,75 BAD (HHHHHH) +Size 76: 46,76 BAD (HHHHHH) +Size 77: 46,77 BAD (HHHHHH) +Size 78: 47,78 BAD (HHHHHH) +Size 79: 48,79 BAD (HHHHHH) +Size 80: 48,80 BAD (HHHHHH) +Size 81: 49,81 BAD (HHHHHH) +Size 82: 49,82 BAD (HHHHHH) +Size 83: 50,83 BAD (HHHHHH) +Size 84: 51,84 BAD (HHHHHH) +Size 85: 51,85 BAD (HHHHHH) +Size 86: 52,86 BAD (HHHHHH) +Size 87: 52,87 BAD (HHHHHH) +Size 88: 53,88 BAD (HHHHHH) +Size 89: 54,89 BAD (HHHHHH) +Size 90: 54,90 BAD (HHHHHH) +Size 91: 55,91 BAD (HHHHHH) +Size 92: 55,92 BAD (HHHHHH) +Size 93: 56,93 BAD (HHHHHH) +Size 94: 57,94 BAD (HHHHHH) +Size 95: 57,95 BAD (HHHHHH) +Size 96: 58,96 BAD (HHHHHH) +Size 97: 58,97 BAD (HHHHHH) +Size 98: 59,98 BAD (HHHHHH) +Size 99: 60,99 BAD (HHHHHH) +Size 100: 60,100 BAD (HHHHHH) + +Windows 10 14342 New Console +---------------------------- + +Size 1: 1,1 BAD (HHHHHH) +Size 2: 1,2 BAD (HHHHHH) +Size 3: 2,3 BAD (HHHHHH) +Size 4: 2,4 BAD (HHHHHH) +Size 5: 3,5 BAD (HHHHHH) +Size 6: 4,6 BAD (HHHHHH) +Size 7: 4,7 BAD (HHHHHH) +Size 8: 5,8 BAD (HHHHHH) +Size 9: 5,9 BAD (HHHHHH) +Size 10: 6,10 BAD (HHHHHH) +Size 11: 7,11 BAD (HHHHHH) +Size 12: 7,12 BAD (HHHHHH) +Size 13: 8,13 BAD (HHHHHH) +Size 14: 8,14 BAD (HHHHHH) +Size 15: 9,15 BAD (HHHHHH) +Size 16: 10,16 BAD (HHHHHH) +Size 17: 10,17 BAD (HHHHHH) +Size 18: 11,18 BAD (HHHHHH) +Size 19: 11,19 BAD (HHHHHH) +Size 20: 12,20 BAD (HHHHHH) +Size 21: 13,21 BAD (HHHHHH) +Size 22: 13,22 BAD (HHHHHH) +Size 23: 14,23 BAD (HHHHHH) +Size 24: 14,24 BAD (HHHHHH) +Size 25: 15,25 BAD (HHHHHH) +Size 26: 16,26 BAD (HHHHHH) +Size 27: 16,27 BAD (HHHHHH) +Size 28: 17,28 BAD (HHHHHH) +Size 29: 17,29 BAD (HHHHHH) +Size 30: 18,30 BAD (HHHHHH) +Size 31: 19,31 BAD (HHHHHH) +Size 32: 19,32 BAD (HHHHHH) +Size 33: 20,33 BAD (HHHHHH) +Size 34: 20,34 BAD (HHHHHH) +Size 35: 21,35 BAD (HHHHHH) +Size 36: 22,36 BAD (HHHHHH) +Size 37: 22,37 BAD (HHHHHH) +Size 38: 23,38 BAD (HHHHHH) +Size 39: 23,39 BAD (HHHHHH) +Size 40: 24,40 BAD (HHHHHH) +Size 41: 25,41 BAD (HHHHHH) +Size 42: 25,42 BAD (HHHHHH) +Size 43: 26,43 BAD (HHHHHH) +Size 44: 27,44 BAD (HHHHHH) +Size 45: 27,45 BAD (HHHHHH) +Size 46: 28,46 BAD (HHHHHH) +Size 47: 28,47 BAD (HHHHHH) +Size 48: 29,48 BAD (HHHHHH) +Size 49: 30,49 BAD (HHHHHH) +Size 50: 30,50 BAD (HHHHHH) +Size 51: 31,51 BAD (HHHHHH) +Size 52: 31,52 BAD (HHHHHH) +Size 53: 32,53 BAD (HHHHHH) +Size 54: 33,54 BAD (HHHHHH) +Size 55: 33,55 BAD (HHHHHH) +Size 56: 34,56 BAD (HHHHHH) +Size 57: 34,57 BAD (HHHHHH) +Size 58: 35,58 BAD (HHHHHH) +Size 59: 36,59 BAD (HHHHHH) +Size 60: 36,60 BAD (HHHHHH) +Size 61: 37,61 BAD (HHHHHH) +Size 62: 37,62 BAD (HHHHHH) +Size 63: 38,63 BAD (HHHHHH) +Size 64: 39,64 BAD (HHHHHH) +Size 65: 39,65 BAD (HHHHHH) +Size 66: 40,66 BAD (HHHHHH) +Size 67: 40,67 BAD (HHHHHH) +Size 68: 41,68 BAD (HHHHHH) +Size 69: 42,69 BAD (HHHHHH) +Size 70: 42,70 BAD (HHHHHH) +Size 71: 43,71 BAD (HHHHHH) +Size 72: 43,72 BAD (HHHHHH) +Size 73: 44,73 BAD (HHHHHH) +Size 74: 45,74 BAD (HHHHHH) +Size 75: 45,75 BAD (HHHHHH) +Size 76: 46,76 BAD (HHHHHH) +Size 77: 46,77 BAD (HHHHHH) +Size 78: 47,78 BAD (HHHHHH) +Size 79: 48,79 BAD (HHHHHH) +Size 80: 48,80 BAD (HHHHHH) +Size 81: 49,81 BAD (HHHHHH) +Size 82: 49,82 BAD (HHHHHH) +Size 83: 50,83 BAD (HHHHHH) +Size 84: 51,84 BAD (HHHHHH) +Size 85: 51,85 BAD (HHHHHH) +Size 86: 52,86 BAD (HHHHHH) +Size 87: 52,87 BAD (HHHHHH) +Size 88: 53,88 BAD (HHHHHH) +Size 89: 54,89 BAD (HHHHHH) +Size 90: 54,90 BAD (HHHHHH) +Size 91: 55,91 BAD (HHHHHH) +Size 92: 55,92 BAD (HHHHHH) +Size 93: 56,93 BAD (HHHHHH) +Size 94: 57,94 BAD (HHHHHH) +Size 95: 57,95 BAD (HHHHHH) +Size 96: 58,96 BAD (HHHHHH) +Size 97: 58,97 BAD (HHHHHH) +Size 98: 59,98 BAD (HHHHHH) +Size 99: 60,99 BAD (HHHHHH) +Size 100: 60,100 BAD (HHHHHH) diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt new file mode 100644 index 0000000000..ed3637eac1 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt @@ -0,0 +1,630 @@ +======================================= +Code Page 932, Japanese, MS Gothic font +======================================= + +Options: -face-gothic -family 0x36 +Chars: A2 A3 2014 3044 30FC 4000 + +Vista +----- + +Size 1: 1,2 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 BAD (FFFFHH) +Size 4: 2,4 OK (HHHFFF) +Size 5: 3,5 OK (HHHFFF) +Size 6: 3,6 OK (HHHFFF) +Size 7: 4,7 OK (HHHFFF) +Size 8: 4,8 OK (HHHFFF) +Size 9: 5,9 OK (HHHFFF) +Size 10: 5,10 OK (HHHFFF) +Size 11: 6,11 OK (HHHFFF) +Size 12: 6,12 OK (HHHFFF) +Size 13: 7,13 OK (HHHFFF) +Size 14: 7,14 BAD (HHHFHH) +Size 15: 8,15 OK (HHHFFF) +Size 16: 8,16 BAD (HHHFHH) +Size 17: 9,17 OK (HHHFFF) +Size 18: 9,18 BAD (HHHFHH) +Size 19: 10,19 OK (HHHFFF) +Size 20: 10,20 BAD (HHHFHH) +Size 21: 11,21 OK (HHHFFF) +Size 22: 11,22 BAD (HHHFHH) +Size 23: 12,23 BAD (HHHFHH) +Size 24: 12,24 BAD (HHHFHH) +Size 25: 13,25 BAD (HHHFHH) +Size 26: 13,26 BAD (HHHFHH) +Size 27: 14,27 BAD (HHHFHH) +Size 28: 14,28 BAD (HHHFHH) +Size 29: 15,29 BAD (HHHFHH) +Size 30: 15,30 BAD (HHHFHH) +Size 31: 16,31 BAD (HHHFHH) +Size 32: 16,33 BAD (HHHFHH) +Size 33: 17,33 BAD (HHHFHH) +Size 34: 17,34 BAD (HHHFHH) +Size 35: 18,35 BAD (HHHFHH) +Size 36: 18,36 BAD (HHHFHH) +Size 37: 19,37 BAD (HHHFHH) +Size 38: 19,38 BAD (HHHFHH) +Size 39: 20,39 BAD (HHHFHH) +Size 40: 20,40 BAD (HHHFHH) +Size 41: 21,41 BAD (HHHFHH) +Size 42: 21,42 BAD (HHHFHH) +Size 43: 22,43 BAD (HHHFHH) +Size 44: 22,44 BAD (HHHFHH) +Size 45: 23,45 BAD (HHHFHH) +Size 46: 23,46 BAD (HHHFHH) +Size 47: 24,47 BAD (HHHFHH) +Size 48: 24,48 BAD (HHHFHH) +Size 49: 25,49 BAD (HHHFHH) +Size 50: 25,50 BAD (HHHFHH) +Size 51: 26,51 BAD (HHHFHH) +Size 52: 26,52 BAD (HHHFHH) +Size 53: 27,53 BAD (HHHFHH) +Size 54: 27,54 BAD (HHHFHH) +Size 55: 28,55 BAD (HHHFHH) +Size 56: 28,56 BAD (HHHFHH) +Size 57: 29,57 BAD (HHHFHH) +Size 58: 29,58 BAD (HHHFHH) +Size 59: 30,59 BAD (HHHFHH) +Size 60: 30,60 BAD (HHHFHH) +Size 61: 31,61 BAD (HHHFHH) +Size 62: 31,62 BAD (HHHFHH) +Size 63: 32,63 BAD (HHHFHH) +Size 64: 32,64 BAD (HHHFHH) +Size 65: 33,65 BAD (HHHFHH) +Size 66: 33,66 BAD (HHHFHH) +Size 67: 34,67 BAD (HHHFHH) +Size 68: 34,68 BAD (HHHFHH) +Size 69: 35,69 BAD (HHHFHH) +Size 70: 35,70 BAD (HHHFHH) +Size 71: 36,71 BAD (HHHFHH) +Size 72: 36,72 BAD (HHHFHH) +Size 73: 37,73 BAD (HHHFHH) +Size 74: 37,74 BAD (HHHFHH) +Size 75: 38,75 BAD (HHHFHH) +Size 76: 38,76 BAD (HHHFHH) +Size 77: 39,77 BAD (HHHFHH) +Size 78: 39,78 BAD (HHHFHH) +Size 79: 40,79 BAD (HHHFHH) +Size 80: 40,80 BAD (HHHFHH) +Size 81: 41,81 BAD (HHHFHH) +Size 82: 41,82 BAD (HHHFHH) +Size 83: 42,83 BAD (HHHFHH) +Size 84: 42,84 BAD (HHHFHH) +Size 85: 43,85 BAD (HHHFHH) +Size 86: 43,86 BAD (HHHFHH) +Size 87: 44,87 BAD (HHHFHH) +Size 88: 44,88 BAD (HHHFHH) +Size 89: 45,89 BAD (HHHFHH) +Size 90: 45,90 BAD (HHHFHH) +Size 91: 46,91 BAD (HHHFHH) +Size 92: 46,92 BAD (HHHFHH) +Size 93: 47,93 BAD (HHHFHH) +Size 94: 47,94 BAD (HHHFHH) +Size 95: 48,95 BAD (HHHFHH) +Size 96: 48,97 BAD (HHHFHH) +Size 97: 49,97 BAD (HHHFHH) +Size 98: 49,98 BAD (HHHFHH) +Size 99: 50,99 BAD (HHHFHH) +Size 100: 50,100 BAD (HHHFHH) + +Windows 7 +--------- + +Size 1: 1,2 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 BAD (FFFFHH) +Size 4: 2,4 OK (HHHFFF) +Size 5: 3,5 OK (HHHFFF) +Size 6: 3,6 OK (HHHFFF) +Size 7: 4,7 OK (HHHFFF) +Size 8: 4,8 OK (HHHFFF) +Size 9: 5,9 OK (HHHFFF) +Size 10: 5,10 OK (HHHFFF) +Size 11: 6,11 OK (HHHFFF) +Size 12: 6,12 OK (HHHFFF) +Size 13: 7,13 OK (HHHFFF) +Size 14: 7,14 BAD (FFFFFF) +Size 15: 8,15 OK (HHHFFF) +Size 16: 8,16 BAD (FFFFFF) +Size 17: 9,17 OK (HHHFFF) +Size 18: 9,18 BAD (FFFFFF) +Size 19: 10,19 OK (HHHFFF) +Size 20: 10,20 BAD (FFFFFF) +Size 21: 11,21 OK (HHHFFF) +Size 22: 11,22 BAD (FFFFFF) +Size 23: 12,23 BAD (FFFFFF) +Size 24: 12,24 BAD (FFFFFF) +Size 25: 13,25 BAD (FFFFFF) +Size 26: 13,26 BAD (FFFFFF) +Size 27: 14,27 BAD (FFFFFF) +Size 28: 14,28 BAD (FFFFFF) +Size 29: 15,29 BAD (FFFFFF) +Size 30: 15,30 BAD (FFFFFF) +Size 31: 16,31 BAD (FFFFFF) +Size 32: 16,33 BAD (FFFFFF) +Size 33: 17,33 BAD (FFFFFF) +Size 34: 17,34 BAD (FFFFFF) +Size 35: 18,35 BAD (FFFFFF) +Size 36: 18,36 BAD (FFFFFF) +Size 37: 19,37 BAD (FFFFFF) +Size 38: 19,38 BAD (FFFFFF) +Size 39: 20,39 BAD (FFFFFF) +Size 40: 20,40 BAD (FFFFFF) +Size 41: 21,41 BAD (FFFFFF) +Size 42: 21,42 BAD (FFFFFF) +Size 43: 22,43 BAD (FFFFFF) +Size 44: 22,44 BAD (FFFFFF) +Size 45: 23,45 BAD (FFFFFF) +Size 46: 23,46 BAD (FFFFFF) +Size 47: 24,47 BAD (FFFFFF) +Size 48: 24,48 BAD (FFFFFF) +Size 49: 25,49 BAD (FFFFFF) +Size 50: 25,50 BAD (FFFFFF) +Size 51: 26,51 BAD (FFFFFF) +Size 52: 26,52 BAD (FFFFFF) +Size 53: 27,53 BAD (FFFFFF) +Size 54: 27,54 BAD (FFFFFF) +Size 55: 28,55 BAD (FFFFFF) +Size 56: 28,56 BAD (FFFFFF) +Size 57: 29,57 BAD (FFFFFF) +Size 58: 29,58 BAD (FFFFFF) +Size 59: 30,59 BAD (FFFFFF) +Size 60: 30,60 BAD (FFFFFF) +Size 61: 31,61 BAD (FFFFFF) +Size 62: 31,62 BAD (FFFFFF) +Size 63: 32,63 BAD (FFFFFF) +Size 64: 32,64 BAD (FFFFFF) +Size 65: 33,65 BAD (FFFFFF) +Size 66: 33,66 BAD (FFFFFF) +Size 67: 34,67 BAD (FFFFFF) +Size 68: 34,68 BAD (FFFFFF) +Size 69: 35,69 BAD (FFFFFF) +Size 70: 35,70 BAD (FFFFFF) +Size 71: 36,71 BAD (FFFFFF) +Size 72: 36,72 BAD (FFFFFF) +Size 73: 37,73 BAD (FFFFFF) +Size 74: 37,74 BAD (FFFFFF) +Size 75: 38,75 BAD (FFFFFF) +Size 76: 38,76 BAD (FFFFFF) +Size 77: 39,77 BAD (FFFFFF) +Size 78: 39,78 BAD (FFFFFF) +Size 79: 40,79 BAD (FFFFFF) +Size 80: 40,80 BAD (FFFFFF) +Size 81: 41,81 BAD (FFFFFF) +Size 82: 41,82 BAD (FFFFFF) +Size 83: 42,83 BAD (FFFFFF) +Size 84: 42,84 BAD (FFFFFF) +Size 85: 43,85 BAD (FFFFFF) +Size 86: 43,86 BAD (FFFFFF) +Size 87: 44,87 BAD (FFFFFF) +Size 88: 44,88 BAD (FFFFFF) +Size 89: 45,89 BAD (FFFFFF) +Size 90: 45,90 BAD (FFFFFF) +Size 91: 46,91 BAD (FFFFFF) +Size 92: 46,92 BAD (FFFFFF) +Size 93: 47,93 BAD (FFFFFF) +Size 94: 47,94 BAD (FFFFFF) +Size 95: 48,95 BAD (FFFFFF) +Size 96: 48,97 BAD (FFFFFF) +Size 97: 49,97 BAD (FFFFFF) +Size 98: 49,98 BAD (FFFFFF) +Size 99: 50,99 BAD (FFFFFF) +Size 100: 50,100 BAD (FFFFFF) + +Windows 8 +--------- + +Size 1: 1,2 BAD (FFFFHH) +Size 2: 1,2 BAD (FFFFHH) +Size 3: 2,3 BAD (FFFFFF) +Size 4: 2,4 BAD (FFFFHH) +Size 5: 3,5 BAD (FFFFFF) +Size 6: 3,6 BAD (FFFFHH) +Size 7: 4,7 BAD (FFFFFF) +Size 8: 4,8 BAD (FFFFHH) +Size 9: 5,9 BAD (FFFFFF) +Size 10: 5,10 BAD (FFFFHH) +Size 11: 6,11 BAD (FFFFFF) +Size 12: 6,12 BAD (FFFFHH) +Size 13: 7,13 BAD (FFFFFF) +Size 14: 7,14 BAD (FFFFHH) +Size 15: 8,15 BAD (FFFFFF) +Size 16: 8,16 BAD (FFFFHH) +Size 17: 9,17 BAD (FFFFFF) +Size 18: 9,18 BAD (FFFFHH) +Size 19: 10,19 BAD (FFFFFF) +Size 20: 10,20 BAD (FFFFFF) +Size 21: 11,21 BAD (FFFFFF) +Size 22: 11,22 BAD (FFFFFF) +Size 23: 12,23 BAD (FFFFFF) +Size 24: 12,24 BAD (FFFFFF) +Size 25: 13,25 BAD (FFFFFF) +Size 26: 13,26 BAD (FFFFFF) +Size 27: 14,27 BAD (FFFFFF) +Size 28: 14,28 BAD (FFFFFF) +Size 29: 15,29 BAD (FFFFFF) +Size 30: 15,30 BAD (FFFFFF) +Size 31: 16,31 BAD (FFFFFF) +Size 32: 16,33 BAD (FFFFFF) +Size 33: 17,33 BAD (FFFFFF) +Size 34: 17,34 BAD (FFFFFF) +Size 35: 18,35 BAD (FFFFFF) +Size 36: 18,36 BAD (FFFFFF) +Size 37: 19,37 BAD (FFFFFF) +Size 38: 19,38 BAD (FFFFFF) +Size 39: 20,39 BAD (FFFFFF) +Size 40: 20,40 BAD (FFFFFF) +Size 41: 21,41 BAD (FFFFFF) +Size 42: 21,42 BAD (FFFFFF) +Size 43: 22,43 BAD (FFFFFF) +Size 44: 22,44 BAD (FFFFFF) +Size 45: 23,45 BAD (FFFFFF) +Size 46: 23,46 BAD (FFFFFF) +Size 47: 24,47 BAD (FFFFFF) +Size 48: 24,48 BAD (FFFFFF) +Size 49: 25,49 BAD (FFFFFF) +Size 50: 25,50 BAD (FFFFFF) +Size 51: 26,51 BAD (FFFFFF) +Size 52: 26,52 BAD (FFFFFF) +Size 53: 27,53 BAD (FFFFFF) +Size 54: 27,54 BAD (FFFFFF) +Size 55: 28,55 BAD (FFFFFF) +Size 56: 28,56 BAD (FFFFFF) +Size 57: 29,57 BAD (FFFFFF) +Size 58: 29,58 BAD (FFFFFF) +Size 59: 30,59 BAD (FFFFFF) +Size 60: 30,60 BAD (FFFFFF) +Size 61: 31,61 BAD (FFFFFF) +Size 62: 31,62 BAD (FFFFFF) +Size 63: 32,63 BAD (FFFFFF) +Size 64: 32,64 BAD (FFFFFF) +Size 65: 33,65 BAD (FFFFFF) +Size 66: 33,66 BAD (FFFFFF) +Size 67: 34,67 BAD (FFFFFF) +Size 68: 34,68 BAD (FFFFFF) +Size 69: 35,69 BAD (FFFFFF) +Size 70: 35,70 BAD (FFFFFF) +Size 71: 36,71 BAD (FFFFFF) +Size 72: 36,72 BAD (FFFFFF) +Size 73: 37,73 BAD (FFFFFF) +Size 74: 37,74 BAD (FFFFFF) +Size 75: 38,75 BAD (FFFFFF) +Size 76: 38,76 BAD (FFFFFF) +Size 77: 39,77 BAD (FFFFFF) +Size 78: 39,78 BAD (FFFFFF) +Size 79: 40,79 BAD (FFFFFF) +Size 80: 40,80 BAD (FFFFFF) +Size 81: 41,81 BAD (FFFFFF) +Size 82: 41,82 BAD (FFFFFF) +Size 83: 42,83 BAD (FFFFFF) +Size 84: 42,84 BAD (FFFFFF) +Size 85: 43,85 BAD (FFFFFF) +Size 86: 43,86 BAD (FFFFFF) +Size 87: 44,87 BAD (FFFFFF) +Size 88: 44,88 BAD (FFFFFF) +Size 89: 45,89 BAD (FFFFFF) +Size 90: 45,90 BAD (FFFFFF) +Size 91: 46,91 BAD (FFFFFF) +Size 92: 46,92 BAD (FFFFFF) +Size 93: 47,93 BAD (FFFFFF) +Size 94: 47,94 BAD (FFFFFF) +Size 95: 48,95 BAD (FFFFFF) +Size 96: 48,97 BAD (FFFFFF) +Size 97: 49,97 BAD (FFFFFF) +Size 98: 49,98 BAD (FFFFFF) +Size 99: 50,99 BAD (FFFFFF) +Size 100: 50,100 BAD (FFFFFF) + +Windows 8.1 +----------- + +Size 1: 1,2 BAD (FFFFHH) +Size 2: 1,2 BAD (FFFFHH) +Size 3: 2,3 BAD (FFFFFF) +Size 4: 2,4 BAD (FFFFHH) +Size 5: 3,5 BAD (FFFFFF) +Size 6: 3,6 BAD (FFFFHH) +Size 7: 4,7 BAD (FFFFFF) +Size 8: 4,8 BAD (FFFFHH) +Size 9: 5,9 BAD (FFFFFF) +Size 10: 5,10 BAD (FFFFHH) +Size 11: 6,11 BAD (FFFFFF) +Size 12: 6,12 BAD (FFFFHH) +Size 13: 7,13 BAD (FFFFFF) +Size 14: 7,14 BAD (FFFFHH) +Size 15: 8,15 BAD (FFFFFF) +Size 16: 8,16 BAD (FFFFHH) +Size 17: 9,17 BAD (FFFFFF) +Size 18: 9,18 BAD (FFFFHH) +Size 19: 10,19 BAD (FFFFFF) +Size 20: 10,20 BAD (FFFFFF) +Size 21: 11,21 BAD (FFFFFF) +Size 22: 11,22 BAD (FFFFFF) +Size 23: 12,23 BAD (FFFFFF) +Size 24: 12,24 BAD (FFFFFF) +Size 25: 13,25 BAD (FFFFFF) +Size 26: 13,26 BAD (FFFFFF) +Size 27: 14,27 BAD (FFFFFF) +Size 28: 14,28 BAD (FFFFFF) +Size 29: 15,29 BAD (FFFFFF) +Size 30: 15,30 BAD (FFFFFF) +Size 31: 16,31 BAD (FFFFFF) +Size 32: 16,33 BAD (FFFFFF) +Size 33: 17,33 BAD (FFFFFF) +Size 34: 17,34 BAD (FFFFFF) +Size 35: 18,35 BAD (FFFFFF) +Size 36: 18,36 BAD (FFFFFF) +Size 37: 19,37 BAD (FFFFFF) +Size 38: 19,38 BAD (FFFFFF) +Size 39: 20,39 BAD (FFFFFF) +Size 40: 20,40 BAD (FFFFFF) +Size 41: 21,41 BAD (FFFFFF) +Size 42: 21,42 BAD (FFFFFF) +Size 43: 22,43 BAD (FFFFFF) +Size 44: 22,44 BAD (FFFFFF) +Size 45: 23,45 BAD (FFFFFF) +Size 46: 23,46 BAD (FFFFFF) +Size 47: 24,47 BAD (FFFFFF) +Size 48: 24,48 BAD (FFFFFF) +Size 49: 25,49 BAD (FFFFFF) +Size 50: 25,50 BAD (FFFFFF) +Size 51: 26,51 BAD (FFFFFF) +Size 52: 26,52 BAD (FFFFFF) +Size 53: 27,53 BAD (FFFFFF) +Size 54: 27,54 BAD (FFFFFF) +Size 55: 28,55 BAD (FFFFFF) +Size 56: 28,56 BAD (FFFFFF) +Size 57: 29,57 BAD (FFFFFF) +Size 58: 29,58 BAD (FFFFFF) +Size 59: 30,59 BAD (FFFFFF) +Size 60: 30,60 BAD (FFFFFF) +Size 61: 31,61 BAD (FFFFFF) +Size 62: 31,62 BAD (FFFFFF) +Size 63: 32,63 BAD (FFFFFF) +Size 64: 32,64 BAD (FFFFFF) +Size 65: 33,65 BAD (FFFFFF) +Size 66: 33,66 BAD (FFFFFF) +Size 67: 34,67 BAD (FFFFFF) +Size 68: 34,68 BAD (FFFFFF) +Size 69: 35,69 BAD (FFFFFF) +Size 70: 35,70 BAD (FFFFFF) +Size 71: 36,71 BAD (FFFFFF) +Size 72: 36,72 BAD (FFFFFF) +Size 73: 37,73 BAD (FFFFFF) +Size 74: 37,74 BAD (FFFFFF) +Size 75: 38,75 BAD (FFFFFF) +Size 76: 38,76 BAD (FFFFFF) +Size 77: 39,77 BAD (FFFFFF) +Size 78: 39,78 BAD (FFFFFF) +Size 79: 40,79 BAD (FFFFFF) +Size 80: 40,80 BAD (FFFFFF) +Size 81: 41,81 BAD (FFFFFF) +Size 82: 41,82 BAD (FFFFFF) +Size 83: 42,83 BAD (FFFFFF) +Size 84: 42,84 BAD (FFFFFF) +Size 85: 43,85 BAD (FFFFFF) +Size 86: 43,86 BAD (FFFFFF) +Size 87: 44,87 BAD (FFFFFF) +Size 88: 44,88 BAD (FFFFFF) +Size 89: 45,89 BAD (FFFFFF) +Size 90: 45,90 BAD (FFFFFF) +Size 91: 46,91 BAD (FFFFFF) +Size 92: 46,92 BAD (FFFFFF) +Size 93: 47,93 BAD (FFFFFF) +Size 94: 47,94 BAD (FFFFFF) +Size 95: 48,95 BAD (FFFFFF) +Size 96: 48,97 BAD (FFFFFF) +Size 97: 49,97 BAD (FFFFFF) +Size 98: 49,98 BAD (FFFFFF) +Size 99: 50,99 BAD (FFFFFF) +Size 100: 50,100 BAD (FFFFFF) + +Windows 10 14342 Old Console +---------------------------- + +Size 1: 1,2 BAD (FFFFHH) +Size 2: 1,2 BAD (FFFFHH) +Size 3: 2,3 BAD (FFFFFF) +Size 4: 2,4 BAD (FFFFHH) +Size 5: 3,5 BAD (FFFFFF) +Size 6: 3,6 BAD (FFFFHH) +Size 7: 4,7 BAD (FFFFFF) +Size 8: 4,8 BAD (FFFFHH) +Size 9: 5,9 BAD (FFFFFF) +Size 10: 5,10 BAD (FFFFHH) +Size 11: 6,11 BAD (FFFFFF) +Size 12: 6,12 BAD (FFFFHH) +Size 13: 7,13 BAD (FFFFFF) +Size 14: 7,14 BAD (FFFFHH) +Size 15: 8,15 BAD (FFFFFF) +Size 16: 8,16 BAD (FFFFHH) +Size 17: 9,17 BAD (FFFFFF) +Size 18: 9,18 BAD (FFFFHH) +Size 19: 10,19 BAD (FFFFFF) +Size 20: 10,20 BAD (FFFFFF) +Size 21: 11,21 BAD (FFFFFF) +Size 22: 11,22 BAD (FFFFFF) +Size 23: 12,23 BAD (FFFFFF) +Size 24: 12,24 BAD (FFFFFF) +Size 25: 13,25 BAD (FFFFFF) +Size 26: 13,26 BAD (FFFFFF) +Size 27: 14,27 BAD (FFFFFF) +Size 28: 14,28 BAD (FFFFFF) +Size 29: 15,29 BAD (FFFFFF) +Size 30: 15,30 BAD (FFFFFF) +Size 31: 16,31 BAD (FFFFFF) +Size 32: 16,33 BAD (FFFFFF) +Size 33: 17,33 BAD (FFFFFF) +Size 34: 17,34 BAD (FFFFFF) +Size 35: 18,35 BAD (FFFFFF) +Size 36: 18,36 BAD (FFFFFF) +Size 37: 19,37 BAD (FFFFFF) +Size 38: 19,38 BAD (FFFFFF) +Size 39: 20,39 BAD (FFFFFF) +Size 40: 20,40 BAD (FFFFFF) +Size 41: 21,41 BAD (FFFFFF) +Size 42: 21,42 BAD (FFFFFF) +Size 43: 22,43 BAD (FFFFFF) +Size 44: 22,44 BAD (FFFFFF) +Size 45: 23,45 BAD (FFFFFF) +Size 46: 23,46 BAD (FFFFFF) +Size 47: 24,47 BAD (FFFFFF) +Size 48: 24,48 BAD (FFFFFF) +Size 49: 25,49 BAD (FFFFFF) +Size 50: 25,50 BAD (FFFFFF) +Size 51: 26,51 BAD (FFFFFF) +Size 52: 26,52 BAD (FFFFFF) +Size 53: 27,53 BAD (FFFFFF) +Size 54: 27,54 BAD (FFFFFF) +Size 55: 28,55 BAD (FFFFFF) +Size 56: 28,56 BAD (FFFFFF) +Size 57: 29,57 BAD (FFFFFF) +Size 58: 29,58 BAD (FFFFFF) +Size 59: 30,59 BAD (FFFFFF) +Size 60: 30,60 BAD (FFFFFF) +Size 61: 31,61 BAD (FFFFFF) +Size 62: 31,62 BAD (FFFFFF) +Size 63: 32,63 BAD (FFFFFF) +Size 64: 32,64 BAD (FFFFFF) +Size 65: 33,65 BAD (FFFFFF) +Size 66: 33,66 BAD (FFFFFF) +Size 67: 34,67 BAD (FFFFFF) +Size 68: 34,68 BAD (FFFFFF) +Size 69: 35,69 BAD (FFFFFF) +Size 70: 35,70 BAD (FFFFFF) +Size 71: 36,71 BAD (FFFFFF) +Size 72: 36,72 BAD (FFFFFF) +Size 73: 37,73 BAD (FFFFFF) +Size 74: 37,74 BAD (FFFFFF) +Size 75: 38,75 BAD (FFFFFF) +Size 76: 38,76 BAD (FFFFFF) +Size 77: 39,77 BAD (FFFFFF) +Size 78: 39,78 BAD (FFFFFF) +Size 79: 40,79 BAD (FFFFFF) +Size 80: 40,80 BAD (FFFFFF) +Size 81: 41,81 BAD (FFFFFF) +Size 82: 41,82 BAD (FFFFFF) +Size 83: 42,83 BAD (FFFFFF) +Size 84: 42,84 BAD (FFFFFF) +Size 85: 43,85 BAD (FFFFFF) +Size 86: 43,86 BAD (FFFFFF) +Size 87: 44,87 BAD (FFFFFF) +Size 88: 44,88 BAD (FFFFFF) +Size 89: 45,89 BAD (FFFFFF) +Size 90: 45,90 BAD (FFFFFF) +Size 91: 46,91 BAD (FFFFFF) +Size 92: 46,92 BAD (FFFFFF) +Size 93: 47,93 BAD (FFFFFF) +Size 94: 47,94 BAD (FFFFFF) +Size 95: 48,95 BAD (FFFFFF) +Size 96: 48,97 BAD (FFFFFF) +Size 97: 49,97 BAD (FFFFFF) +Size 98: 49,98 BAD (FFFFFF) +Size 99: 50,99 BAD (FFFFFF) +Size 100: 50,100 BAD (FFFFFF) + +Windows 10 14342 New Console +---------------------------- + +Size 1: 1,1 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 OK (HHHFFF) +Size 4: 2,4 OK (HHHFFF) +Size 5: 3,5 OK (HHHFFF) +Size 6: 3,6 OK (HHHFFF) +Size 7: 4,7 OK (HHHFFF) +Size 8: 4,8 OK (HHHFFF) +Size 9: 5,9 OK (HHHFFF) +Size 10: 5,10 OK (HHHFFF) +Size 11: 6,11 OK (HHHFFF) +Size 12: 6,12 OK (HHHFFF) +Size 13: 7,13 OK (HHHFFF) +Size 14: 7,14 OK (HHHFFF) +Size 15: 8,15 OK (HHHFFF) +Size 16: 8,16 OK (HHHFFF) +Size 17: 9,17 OK (HHHFFF) +Size 18: 9,18 OK (HHHFFF) +Size 19: 10,19 OK (HHHFFF) +Size 20: 10,20 OK (HHHFFF) +Size 21: 11,21 OK (HHHFFF) +Size 22: 11,22 OK (HHHFFF) +Size 23: 12,23 OK (HHHFFF) +Size 24: 12,24 OK (HHHFFF) +Size 25: 13,25 OK (HHHFFF) +Size 26: 13,26 OK (HHHFFF) +Size 27: 14,27 OK (HHHFFF) +Size 28: 14,28 OK (HHHFFF) +Size 29: 15,29 OK (HHHFFF) +Size 30: 15,30 OK (HHHFFF) +Size 31: 16,31 OK (HHHFFF) +Size 32: 16,32 OK (HHHFFF) +Size 33: 17,33 OK (HHHFFF) +Size 34: 17,34 OK (HHHFFF) +Size 35: 18,35 OK (HHHFFF) +Size 36: 18,36 OK (HHHFFF) +Size 37: 19,37 OK (HHHFFF) +Size 38: 19,38 OK (HHHFFF) +Size 39: 20,39 OK (HHHFFF) +Size 40: 20,40 OK (HHHFFF) +Size 41: 21,41 OK (HHHFFF) +Size 42: 21,42 OK (HHHFFF) +Size 43: 22,43 OK (HHHFFF) +Size 44: 22,44 OK (HHHFFF) +Size 45: 23,45 OK (HHHFFF) +Size 46: 23,46 OK (HHHFFF) +Size 47: 24,47 OK (HHHFFF) +Size 48: 24,48 OK (HHHFFF) +Size 49: 25,49 OK (HHHFFF) +Size 50: 25,50 OK (HHHFFF) +Size 51: 26,51 OK (HHHFFF) +Size 52: 26,52 OK (HHHFFF) +Size 53: 27,53 OK (HHHFFF) +Size 54: 27,54 OK (HHHFFF) +Size 55: 28,55 OK (HHHFFF) +Size 56: 28,56 OK (HHHFFF) +Size 57: 29,57 OK (HHHFFF) +Size 58: 29,58 OK (HHHFFF) +Size 59: 30,59 OK (HHHFFF) +Size 60: 30,60 OK (HHHFFF) +Size 61: 31,61 OK (HHHFFF) +Size 62: 31,62 OK (HHHFFF) +Size 63: 32,63 OK (HHHFFF) +Size 64: 32,64 OK (HHHFFF) +Size 65: 33,65 OK (HHHFFF) +Size 66: 33,66 OK (HHHFFF) +Size 67: 34,67 OK (HHHFFF) +Size 68: 34,68 OK (HHHFFF) +Size 69: 35,69 OK (HHHFFF) +Size 70: 35,70 OK (HHHFFF) +Size 71: 36,71 OK (HHHFFF) +Size 72: 36,72 OK (HHHFFF) +Size 73: 37,73 OK (HHHFFF) +Size 74: 37,74 OK (HHHFFF) +Size 75: 38,75 OK (HHHFFF) +Size 76: 38,76 OK (HHHFFF) +Size 77: 39,77 OK (HHHFFF) +Size 78: 39,78 OK (HHHFFF) +Size 79: 40,79 OK (HHHFFF) +Size 80: 40,80 OK (HHHFFF) +Size 81: 41,81 OK (HHHFFF) +Size 82: 41,82 OK (HHHFFF) +Size 83: 42,83 OK (HHHFFF) +Size 84: 42,84 OK (HHHFFF) +Size 85: 43,85 OK (HHHFFF) +Size 86: 43,86 OK (HHHFFF) +Size 87: 44,87 OK (HHHFFF) +Size 88: 44,88 OK (HHHFFF) +Size 89: 45,89 OK (HHHFFF) +Size 90: 45,90 OK (HHHFFF) +Size 91: 46,91 OK (HHHFFF) +Size 92: 46,92 OK (HHHFFF) +Size 93: 47,93 OK (HHHFFF) +Size 94: 47,94 OK (HHHFFF) +Size 95: 48,95 OK (HHHFFF) +Size 96: 48,96 OK (HHHFFF) +Size 97: 49,97 OK (HHHFFF) +Size 98: 49,98 OK (HHHFFF) +Size 99: 50,99 OK (HHHFFF) +Size 100: 50,100 OK (HHHFFF) diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt new file mode 100644 index 0000000000..43210dac51 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt @@ -0,0 +1,630 @@ +========================================================== +Code Page 936, Chinese Simplified (China/PRC), SimSun font +========================================================== + +Options: -face-simsun -family 0x36 +Chars: A2 A3 2014 3044 30FC 4000 + +Vista +----- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,3 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (HHHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (HHHFHH) +Size 8: 4,9 GOOD (HHFFFF) +Size 9: 5,10 BAD (HHHFHH) +Size 10: 5,11 GOOD (HHFFFF) +Size 11: 6,13 BAD (HHHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,15 BAD (HHHFHH) +Size 14: 7,16 GOOD (HHFFFF) +Size 15: 8,17 BAD (HHHFHH) +Size 16: 8,18 GOOD (HHFFFF) +Size 17: 9,19 BAD (HHHFHH) +Size 18: 9,21 GOOD (HHFFFF) +Size 19: 10,22 BAD (HHHFHH) +Size 20: 10,23 GOOD (HHFFFF) +Size 21: 11,24 BAD (HHHFHH) +Size 22: 11,25 GOOD (HHFFFF) +Size 23: 12,26 BAD (HHHFHH) +Size 24: 12,27 GOOD (HHFFFF) +Size 25: 13,29 BAD (HHHFHH) +Size 26: 13,30 GOOD (HHFFFF) +Size 27: 14,31 BAD (HHHFHH) +Size 28: 14,32 GOOD (HHFFFF) +Size 29: 15,33 BAD (HHHFHH) +Size 30: 15,34 GOOD (HHFFFF) +Size 31: 16,35 BAD (HHHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,38 BAD (HHHFHH) +Size 34: 17,39 GOOD (HHFFFF) +Size 35: 18,40 BAD (HHHFHH) +Size 36: 18,41 GOOD (HHFFFF) +Size 37: 19,42 BAD (HHHFHH) +Size 38: 19,43 GOOD (HHFFFF) +Size 39: 20,44 BAD (HHHFHH) +Size 40: 20,46 GOOD (HHFFFF) +Size 41: 21,47 BAD (HHHFHH) +Size 42: 21,48 GOOD (HHFFFF) +Size 43: 22,49 BAD (HHHFHH) +Size 44: 22,50 GOOD (HHFFFF) +Size 45: 23,51 BAD (HHHFHH) +Size 46: 23,52 GOOD (HHFFFF) +Size 47: 24,54 BAD (HHHFHH) +Size 48: 24,55 GOOD (HHFFFF) +Size 49: 25,56 BAD (HHHFHH) +Size 50: 25,57 GOOD (HHFFFF) +Size 51: 26,58 BAD (HHHFHH) +Size 52: 26,59 GOOD (HHFFFF) +Size 53: 27,60 BAD (HHHFHH) +Size 54: 27,62 GOOD (HHFFFF) +Size 55: 28,63 BAD (HHHFHH) +Size 56: 28,64 GOOD (HHFFFF) +Size 57: 29,65 BAD (HHHFHH) +Size 58: 29,66 GOOD (HHFFFF) +Size 59: 30,67 BAD (HHHFHH) +Size 60: 30,68 GOOD (HHFFFF) +Size 61: 31,70 BAD (HHHFHH) +Size 62: 31,71 GOOD (HHFFFF) +Size 63: 32,72 BAD (HHHFHH) +Size 64: 32,73 GOOD (HHFFFF) +Size 65: 33,74 GOOD (HHFFFF) +Size 66: 33,75 GOOD (HHFFFF) +Size 67: 34,76 GOOD (HHFFFF) +Size 68: 34,78 GOOD (HHFFFF) +Size 69: 35,79 GOOD (HHFFFF) +Size 70: 35,80 GOOD (HHFFFF) +Size 71: 36,81 GOOD (HHFFFF) +Size 72: 36,82 GOOD (HHFFFF) +Size 73: 37,83 GOOD (HHFFFF) +Size 74: 37,84 GOOD (HHFFFF) +Size 75: 38,86 GOOD (HHFFFF) +Size 76: 38,87 GOOD (HHFFFF) +Size 77: 39,88 GOOD (HHFFFF) +Size 78: 39,89 GOOD (HHFFFF) +Size 79: 40,90 GOOD (HHFFFF) +Size 80: 40,91 GOOD (HHFFFF) +Size 81: 41,92 GOOD (HHFFFF) +Size 82: 41,94 GOOD (HHFFFF) +Size 83: 42,95 GOOD (HHFFFF) +Size 84: 42,96 GOOD (HHFFFF) +Size 85: 43,97 GOOD (HHFFFF) +Size 86: 43,98 GOOD (HHFFFF) +Size 87: 44,99 GOOD (HHFFFF) +Size 88: 44,100 GOOD (HHFFFF) +Size 89: 45,102 GOOD (HHFFFF) +Size 90: 45,103 GOOD (HHFFFF) +Size 91: 46,104 GOOD (HHFFFF) +Size 92: 46,105 GOOD (HHFFFF) +Size 93: 47,106 GOOD (HHFFFF) +Size 94: 47,107 GOOD (HHFFFF) +Size 95: 48,108 GOOD (HHFFFF) +Size 96: 48,111 GOOD (HHFFFF) +Size 97: 49,111 GOOD (HHFFFF) +Size 98: 49,112 GOOD (HHFFFF) +Size 99: 50,113 GOOD (HHFFFF) +Size 100: 50,114 GOOD (HHFFFF) + +Windows 7 +--------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,3 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,9 GOOD (HHFFFF) +Size 9: 5,10 BAD (FFHFHH) +Size 10: 5,11 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,15 BAD (FFHFHH) +Size 14: 7,16 GOOD (HHFFFF) +Size 15: 8,17 BAD (FFHFHH) +Size 16: 8,18 GOOD (HHFFFF) +Size 17: 9,19 BAD (FFHFHH) +Size 18: 9,21 GOOD (HHFFFF) +Size 19: 10,22 BAD (FFHFHH) +Size 20: 10,23 GOOD (HHFFFF) +Size 21: 11,24 BAD (FFHFHH) +Size 22: 11,25 GOOD (HHFFFF) +Size 23: 12,26 BAD (FFHFHH) +Size 24: 12,27 GOOD (HHFFFF) +Size 25: 13,29 BAD (FFHFHH) +Size 26: 13,30 GOOD (HHFFFF) +Size 27: 14,31 BAD (FFHFHH) +Size 28: 14,32 GOOD (HHFFFF) +Size 29: 15,33 BAD (FFHFHH) +Size 30: 15,34 GOOD (HHFFFF) +Size 31: 16,35 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,38 BAD (FFHFHH) +Size 34: 17,39 GOOD (HHFFFF) +Size 35: 18,40 BAD (FFHFHH) +Size 36: 18,41 GOOD (HHFFFF) +Size 37: 19,42 BAD (FFHFHH) +Size 38: 19,43 GOOD (HHFFFF) +Size 39: 20,44 BAD (FFHFHH) +Size 40: 20,46 GOOD (HHFFFF) +Size 41: 21,47 BAD (FFHFHH) +Size 42: 21,48 GOOD (HHFFFF) +Size 43: 22,49 BAD (FFHFHH) +Size 44: 22,50 GOOD (HHFFFF) +Size 45: 23,51 BAD (FFHFHH) +Size 46: 23,52 GOOD (HHFFFF) +Size 47: 24,54 BAD (FFHFHH) +Size 48: 24,55 GOOD (HHFFFF) +Size 49: 25,56 BAD (FFHFHH) +Size 50: 25,57 GOOD (HHFFFF) +Size 51: 26,58 BAD (FFHFHH) +Size 52: 26,59 GOOD (HHFFFF) +Size 53: 27,60 BAD (FFHFHH) +Size 54: 27,62 GOOD (HHFFFF) +Size 55: 28,63 BAD (FFHFHH) +Size 56: 28,64 GOOD (HHFFFF) +Size 57: 29,65 BAD (FFHFHH) +Size 58: 29,66 GOOD (HHFFFF) +Size 59: 30,67 BAD (FFHFHH) +Size 60: 30,68 GOOD (HHFFFF) +Size 61: 31,70 BAD (FFHFHH) +Size 62: 31,71 GOOD (HHFFFF) +Size 63: 32,72 BAD (FFHFHH) +Size 64: 32,73 GOOD (HHFFFF) +Size 65: 33,74 GOOD (HHFFFF) +Size 66: 33,75 GOOD (HHFFFF) +Size 67: 34,76 GOOD (HHFFFF) +Size 68: 34,78 GOOD (HHFFFF) +Size 69: 35,79 GOOD (HHFFFF) +Size 70: 35,80 GOOD (HHFFFF) +Size 71: 36,81 GOOD (HHFFFF) +Size 72: 36,82 GOOD (HHFFFF) +Size 73: 37,83 GOOD (HHFFFF) +Size 74: 37,84 GOOD (HHFFFF) +Size 75: 38,86 GOOD (HHFFFF) +Size 76: 38,87 GOOD (HHFFFF) +Size 77: 39,88 GOOD (HHFFFF) +Size 78: 39,89 GOOD (HHFFFF) +Size 79: 40,90 GOOD (HHFFFF) +Size 80: 40,91 GOOD (HHFFFF) +Size 81: 41,92 GOOD (HHFFFF) +Size 82: 41,94 GOOD (HHFFFF) +Size 83: 42,95 GOOD (HHFFFF) +Size 84: 42,96 GOOD (HHFFFF) +Size 85: 43,97 GOOD (HHFFFF) +Size 86: 43,98 GOOD (HHFFFF) +Size 87: 44,99 GOOD (HHFFFF) +Size 88: 44,100 GOOD (HHFFFF) +Size 89: 45,102 GOOD (HHFFFF) +Size 90: 45,103 GOOD (HHFFFF) +Size 91: 46,104 GOOD (HHFFFF) +Size 92: 46,105 GOOD (HHFFFF) +Size 93: 47,106 GOOD (HHFFFF) +Size 94: 47,107 GOOD (HHFFFF) +Size 95: 48,108 GOOD (HHFFFF) +Size 96: 48,111 GOOD (HHFFFF) +Size 97: 49,111 GOOD (HHFFFF) +Size 98: 49,112 GOOD (HHFFFF) +Size 99: 50,113 GOOD (HHFFFF) +Size 100: 50,114 GOOD (HHFFFF) + +Windows 8 +--------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,3 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,9 GOOD (HHFFFF) +Size 9: 5,10 BAD (FFHFHH) +Size 10: 5,11 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,15 BAD (FFHFHH) +Size 14: 7,16 GOOD (HHFFFF) +Size 15: 8,17 BAD (FFHFHH) +Size 16: 8,18 GOOD (HHFFFF) +Size 17: 9,19 BAD (FFHFHH) +Size 18: 9,21 GOOD (HHFFFF) +Size 19: 10,22 BAD (FFHFHH) +Size 20: 10,23 GOOD (HHFFFF) +Size 21: 11,24 BAD (FFHFHH) +Size 22: 11,25 GOOD (HHFFFF) +Size 23: 12,26 BAD (FFHFHH) +Size 24: 12,27 GOOD (HHFFFF) +Size 25: 13,29 BAD (FFHFHH) +Size 26: 13,30 GOOD (HHFFFF) +Size 27: 14,31 BAD (FFHFHH) +Size 28: 14,32 GOOD (HHFFFF) +Size 29: 15,33 BAD (FFHFHH) +Size 30: 15,34 GOOD (HHFFFF) +Size 31: 16,35 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,38 BAD (FFHFHH) +Size 34: 17,39 GOOD (HHFFFF) +Size 35: 18,40 BAD (FFHFHH) +Size 36: 18,41 GOOD (HHFFFF) +Size 37: 19,42 BAD (FFHFHH) +Size 38: 19,43 GOOD (HHFFFF) +Size 39: 20,44 BAD (FFHFHH) +Size 40: 20,46 GOOD (HHFFFF) +Size 41: 21,47 BAD (FFHFHH) +Size 42: 21,48 GOOD (HHFFFF) +Size 43: 22,49 BAD (FFHFHH) +Size 44: 22,50 GOOD (HHFFFF) +Size 45: 23,51 BAD (FFHFHH) +Size 46: 23,52 GOOD (HHFFFF) +Size 47: 24,54 BAD (FFHFHH) +Size 48: 24,55 GOOD (HHFFFF) +Size 49: 25,56 BAD (FFHFHH) +Size 50: 25,57 GOOD (HHFFFF) +Size 51: 26,58 BAD (FFHFHH) +Size 52: 26,59 GOOD (HHFFFF) +Size 53: 27,60 BAD (FFHFHH) +Size 54: 27,62 GOOD (HHFFFF) +Size 55: 28,63 BAD (FFHFHH) +Size 56: 28,64 GOOD (HHFFFF) +Size 57: 29,65 BAD (FFHFHH) +Size 58: 29,66 GOOD (HHFFFF) +Size 59: 30,67 BAD (FFHFHH) +Size 60: 30,68 GOOD (HHFFFF) +Size 61: 31,70 BAD (FFHFHH) +Size 62: 31,71 GOOD (HHFFFF) +Size 63: 32,72 BAD (FFHFHH) +Size 64: 32,73 GOOD (HHFFFF) +Size 65: 33,74 GOOD (HHFFFF) +Size 66: 33,75 GOOD (HHFFFF) +Size 67: 34,76 GOOD (HHFFFF) +Size 68: 34,78 GOOD (HHFFFF) +Size 69: 35,79 GOOD (HHFFFF) +Size 70: 35,80 GOOD (HHFFFF) +Size 71: 36,81 GOOD (HHFFFF) +Size 72: 36,82 GOOD (HHFFFF) +Size 73: 37,83 GOOD (HHFFFF) +Size 74: 37,84 GOOD (HHFFFF) +Size 75: 38,86 GOOD (HHFFFF) +Size 76: 38,87 GOOD (HHFFFF) +Size 77: 39,88 GOOD (HHFFFF) +Size 78: 39,89 GOOD (HHFFFF) +Size 79: 40,90 GOOD (HHFFFF) +Size 80: 40,91 GOOD (HHFFFF) +Size 81: 41,92 GOOD (HHFFFF) +Size 82: 41,94 GOOD (HHFFFF) +Size 83: 42,95 GOOD (HHFFFF) +Size 84: 42,96 GOOD (HHFFFF) +Size 85: 43,97 GOOD (HHFFFF) +Size 86: 43,98 GOOD (HHFFFF) +Size 87: 44,99 GOOD (HHFFFF) +Size 88: 44,100 GOOD (HHFFFF) +Size 89: 45,102 GOOD (HHFFFF) +Size 90: 45,103 GOOD (HHFFFF) +Size 91: 46,104 GOOD (HHFFFF) +Size 92: 46,105 GOOD (HHFFFF) +Size 93: 47,106 GOOD (HHFFFF) +Size 94: 47,107 GOOD (HHFFFF) +Size 95: 48,108 GOOD (HHFFFF) +Size 96: 48,111 GOOD (HHFFFF) +Size 97: 49,111 GOOD (HHFFFF) +Size 98: 49,112 GOOD (HHFFFF) +Size 99: 50,113 GOOD (HHFFFF) +Size 100: 50,114 GOOD (HHFFFF) + +Windows 8.1 +----------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,3 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,9 GOOD (HHFFFF) +Size 9: 5,10 BAD (FFHFHH) +Size 10: 5,11 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,15 BAD (FFHFHH) +Size 14: 7,16 GOOD (HHFFFF) +Size 15: 8,17 BAD (FFHFHH) +Size 16: 8,18 GOOD (HHFFFF) +Size 17: 9,19 BAD (FFHFHH) +Size 18: 9,21 GOOD (HHFFFF) +Size 19: 10,22 BAD (FFHFHH) +Size 20: 10,23 GOOD (HHFFFF) +Size 21: 11,24 BAD (FFHFHH) +Size 22: 11,25 GOOD (HHFFFF) +Size 23: 12,26 BAD (FFHFHH) +Size 24: 12,27 GOOD (HHFFFF) +Size 25: 13,29 BAD (FFHFHH) +Size 26: 13,30 GOOD (HHFFFF) +Size 27: 14,31 BAD (FFHFHH) +Size 28: 14,32 GOOD (HHFFFF) +Size 29: 15,33 BAD (FFHFHH) +Size 30: 15,34 GOOD (HHFFFF) +Size 31: 16,35 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,38 BAD (FFHFHH) +Size 34: 17,39 GOOD (HHFFFF) +Size 35: 18,40 BAD (FFHFHH) +Size 36: 18,41 GOOD (HHFFFF) +Size 37: 19,42 BAD (FFHFHH) +Size 38: 19,43 GOOD (HHFFFF) +Size 39: 20,44 BAD (FFHFHH) +Size 40: 20,46 GOOD (HHFFFF) +Size 41: 21,47 BAD (FFHFHH) +Size 42: 21,48 GOOD (HHFFFF) +Size 43: 22,49 BAD (FFHFHH) +Size 44: 22,50 GOOD (HHFFFF) +Size 45: 23,51 BAD (FFHFHH) +Size 46: 23,52 GOOD (HHFFFF) +Size 47: 24,54 BAD (FFHFHH) +Size 48: 24,55 GOOD (HHFFFF) +Size 49: 25,56 BAD (FFHFHH) +Size 50: 25,57 GOOD (HHFFFF) +Size 51: 26,58 BAD (FFHFHH) +Size 52: 26,59 GOOD (HHFFFF) +Size 53: 27,60 BAD (FFHFHH) +Size 54: 27,62 GOOD (HHFFFF) +Size 55: 28,63 BAD (FFHFHH) +Size 56: 28,64 GOOD (HHFFFF) +Size 57: 29,65 BAD (FFHFHH) +Size 58: 29,66 GOOD (HHFFFF) +Size 59: 30,67 BAD (FFHFHH) +Size 60: 30,68 GOOD (HHFFFF) +Size 61: 31,70 BAD (FFHFHH) +Size 62: 31,71 GOOD (HHFFFF) +Size 63: 32,72 BAD (FFHFHH) +Size 64: 32,73 GOOD (HHFFFF) +Size 65: 33,74 GOOD (HHFFFF) +Size 66: 33,75 GOOD (HHFFFF) +Size 67: 34,76 GOOD (HHFFFF) +Size 68: 34,78 GOOD (HHFFFF) +Size 69: 35,79 GOOD (HHFFFF) +Size 70: 35,80 GOOD (HHFFFF) +Size 71: 36,81 GOOD (HHFFFF) +Size 72: 36,82 GOOD (HHFFFF) +Size 73: 37,83 GOOD (HHFFFF) +Size 74: 37,84 GOOD (HHFFFF) +Size 75: 38,86 GOOD (HHFFFF) +Size 76: 38,87 GOOD (HHFFFF) +Size 77: 39,88 GOOD (HHFFFF) +Size 78: 39,89 GOOD (HHFFFF) +Size 79: 40,90 GOOD (HHFFFF) +Size 80: 40,91 GOOD (HHFFFF) +Size 81: 41,92 GOOD (HHFFFF) +Size 82: 41,94 GOOD (HHFFFF) +Size 83: 42,95 GOOD (HHFFFF) +Size 84: 42,96 GOOD (HHFFFF) +Size 85: 43,97 GOOD (HHFFFF) +Size 86: 43,98 GOOD (HHFFFF) +Size 87: 44,99 GOOD (HHFFFF) +Size 88: 44,100 GOOD (HHFFFF) +Size 89: 45,102 GOOD (HHFFFF) +Size 90: 45,103 GOOD (HHFFFF) +Size 91: 46,104 GOOD (HHFFFF) +Size 92: 46,105 GOOD (HHFFFF) +Size 93: 47,106 GOOD (HHFFFF) +Size 94: 47,107 GOOD (HHFFFF) +Size 95: 48,108 GOOD (HHFFFF) +Size 96: 48,111 GOOD (HHFFFF) +Size 97: 49,111 GOOD (HHFFFF) +Size 98: 49,112 GOOD (HHFFFF) +Size 99: 50,113 GOOD (HHFFFF) +Size 100: 50,114 GOOD (HHFFFF) + +Windows 10 14342 Old Console +---------------------------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,3 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,9 GOOD (HHFFFF) +Size 9: 5,10 BAD (FFHFHH) +Size 10: 5,11 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,15 BAD (FFHFHH) +Size 14: 7,16 GOOD (HHFFFF) +Size 15: 8,17 BAD (FFHFHH) +Size 16: 8,18 GOOD (HHFFFF) +Size 17: 9,19 BAD (FFHFHH) +Size 18: 9,21 GOOD (HHFFFF) +Size 19: 10,22 BAD (FFHFHH) +Size 20: 10,23 GOOD (HHFFFF) +Size 21: 11,24 BAD (FFHFHH) +Size 22: 11,25 GOOD (HHFFFF) +Size 23: 12,26 BAD (FFHFHH) +Size 24: 12,27 GOOD (HHFFFF) +Size 25: 13,29 BAD (FFHFHH) +Size 26: 13,30 GOOD (HHFFFF) +Size 27: 14,31 BAD (FFHFHH) +Size 28: 14,32 GOOD (HHFFFF) +Size 29: 15,33 BAD (FFHFHH) +Size 30: 15,34 GOOD (HHFFFF) +Size 31: 16,35 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,38 BAD (FFHFHH) +Size 34: 17,39 GOOD (HHFFFF) +Size 35: 18,40 BAD (FFHFHH) +Size 36: 18,41 GOOD (HHFFFF) +Size 37: 19,42 BAD (FFHFHH) +Size 38: 19,43 GOOD (HHFFFF) +Size 39: 20,44 BAD (FFHFHH) +Size 40: 20,46 GOOD (HHFFFF) +Size 41: 21,47 BAD (FFHFHH) +Size 42: 21,48 GOOD (HHFFFF) +Size 43: 22,49 BAD (FFHFHH) +Size 44: 22,50 GOOD (HHFFFF) +Size 45: 23,51 BAD (FFHFHH) +Size 46: 23,52 GOOD (HHFFFF) +Size 47: 24,54 BAD (FFHFHH) +Size 48: 24,55 GOOD (HHFFFF) +Size 49: 25,56 BAD (FFHFHH) +Size 50: 25,57 GOOD (HHFFFF) +Size 51: 26,58 BAD (FFHFHH) +Size 52: 26,59 GOOD (HHFFFF) +Size 53: 27,60 BAD (FFHFHH) +Size 54: 27,62 GOOD (HHFFFF) +Size 55: 28,63 BAD (FFHFHH) +Size 56: 28,64 GOOD (HHFFFF) +Size 57: 29,65 BAD (FFHFHH) +Size 58: 29,66 GOOD (HHFFFF) +Size 59: 30,67 BAD (FFHFHH) +Size 60: 30,68 GOOD (HHFFFF) +Size 61: 31,70 BAD (FFHFHH) +Size 62: 31,71 GOOD (HHFFFF) +Size 63: 32,72 BAD (FFHFHH) +Size 64: 32,73 GOOD (HHFFFF) +Size 65: 33,74 GOOD (HHFFFF) +Size 66: 33,75 GOOD (HHFFFF) +Size 67: 34,76 GOOD (HHFFFF) +Size 68: 34,78 GOOD (HHFFFF) +Size 69: 35,79 GOOD (HHFFFF) +Size 70: 35,80 GOOD (HHFFFF) +Size 71: 36,81 GOOD (HHFFFF) +Size 72: 36,82 GOOD (HHFFFF) +Size 73: 37,83 GOOD (HHFFFF) +Size 74: 37,84 GOOD (HHFFFF) +Size 75: 38,86 GOOD (HHFFFF) +Size 76: 38,87 GOOD (HHFFFF) +Size 77: 39,88 GOOD (HHFFFF) +Size 78: 39,89 GOOD (HHFFFF) +Size 79: 40,90 GOOD (HHFFFF) +Size 80: 40,91 GOOD (HHFFFF) +Size 81: 41,92 GOOD (HHFFFF) +Size 82: 41,94 GOOD (HHFFFF) +Size 83: 42,95 GOOD (HHFFFF) +Size 84: 42,96 GOOD (HHFFFF) +Size 85: 43,97 GOOD (HHFFFF) +Size 86: 43,98 GOOD (HHFFFF) +Size 87: 44,99 GOOD (HHFFFF) +Size 88: 44,100 GOOD (HHFFFF) +Size 89: 45,102 GOOD (HHFFFF) +Size 90: 45,103 GOOD (HHFFFF) +Size 91: 46,104 GOOD (HHFFFF) +Size 92: 46,105 GOOD (HHFFFF) +Size 93: 47,106 GOOD (HHFFFF) +Size 94: 47,107 GOOD (HHFFFF) +Size 95: 48,108 GOOD (HHFFFF) +Size 96: 48,111 GOOD (HHFFFF) +Size 97: 49,111 GOOD (HHFFFF) +Size 98: 49,112 GOOD (HHFFFF) +Size 99: 50,113 GOOD (HHFFFF) +Size 100: 50,114 GOOD (HHFFFF) + +Windows 10 14342 New Console +---------------------------- + +Size 1: 1,1 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,3 GOOD (HHFFFF) +Size 4: 2,4 GOOD (HHFFFF) +Size 5: 3,5 GOOD (HHFFFF) +Size 6: 3,6 GOOD (HHFFFF) +Size 7: 4,7 GOOD (HHFFFF) +Size 8: 4,8 GOOD (HHFFFF) +Size 9: 5,9 GOOD (HHFFFF) +Size 10: 5,10 GOOD (HHFFFF) +Size 11: 6,11 GOOD (HHFFFF) +Size 12: 6,12 GOOD (HHFFFF) +Size 13: 7,13 GOOD (HHFFFF) +Size 14: 7,14 GOOD (HHFFFF) +Size 15: 8,15 GOOD (HHFFFF) +Size 16: 8,16 GOOD (HHFFFF) +Size 17: 9,17 GOOD (HHFFFF) +Size 18: 9,18 GOOD (HHFFFF) +Size 19: 10,19 GOOD (HHFFFF) +Size 20: 10,20 GOOD (HHFFFF) +Size 21: 11,21 GOOD (HHFFFF) +Size 22: 11,22 GOOD (HHFFFF) +Size 23: 12,23 GOOD (HHFFFF) +Size 24: 12,24 GOOD (HHFFFF) +Size 25: 13,25 GOOD (HHFFFF) +Size 26: 13,26 GOOD (HHFFFF) +Size 27: 14,27 GOOD (HHFFFF) +Size 28: 14,28 GOOD (HHFFFF) +Size 29: 15,29 GOOD (HHFFFF) +Size 30: 15,30 GOOD (HHFFFF) +Size 31: 16,31 GOOD (HHFFFF) +Size 32: 16,32 GOOD (HHFFFF) +Size 33: 17,33 GOOD (HHFFFF) +Size 34: 17,34 GOOD (HHFFFF) +Size 35: 18,35 GOOD (HHFFFF) +Size 36: 18,36 GOOD (HHFFFF) +Size 37: 19,37 GOOD (HHFFFF) +Size 38: 19,38 GOOD (HHFFFF) +Size 39: 20,39 GOOD (HHFFFF) +Size 40: 20,40 GOOD (HHFFFF) +Size 41: 21,41 GOOD (HHFFFF) +Size 42: 21,42 GOOD (HHFFFF) +Size 43: 22,43 GOOD (HHFFFF) +Size 44: 22,44 GOOD (HHFFFF) +Size 45: 23,45 GOOD (HHFFFF) +Size 46: 23,46 GOOD (HHFFFF) +Size 47: 24,47 GOOD (HHFFFF) +Size 48: 24,48 GOOD (HHFFFF) +Size 49: 25,49 GOOD (HHFFFF) +Size 50: 25,50 GOOD (HHFFFF) +Size 51: 26,51 GOOD (HHFFFF) +Size 52: 26,52 GOOD (HHFFFF) +Size 53: 27,53 GOOD (HHFFFF) +Size 54: 27,54 GOOD (HHFFFF) +Size 55: 28,55 GOOD (HHFFFF) +Size 56: 28,56 GOOD (HHFFFF) +Size 57: 29,57 GOOD (HHFFFF) +Size 58: 29,58 GOOD (HHFFFF) +Size 59: 30,59 GOOD (HHFFFF) +Size 60: 30,60 GOOD (HHFFFF) +Size 61: 31,61 GOOD (HHFFFF) +Size 62: 31,62 GOOD (HHFFFF) +Size 63: 32,63 GOOD (HHFFFF) +Size 64: 32,64 GOOD (HHFFFF) +Size 65: 33,65 GOOD (HHFFFF) +Size 66: 33,66 GOOD (HHFFFF) +Size 67: 34,67 GOOD (HHFFFF) +Size 68: 34,68 GOOD (HHFFFF) +Size 69: 35,69 GOOD (HHFFFF) +Size 70: 35,70 GOOD (HHFFFF) +Size 71: 36,71 GOOD (HHFFFF) +Size 72: 36,72 GOOD (HHFFFF) +Size 73: 37,73 GOOD (HHFFFF) +Size 74: 37,74 GOOD (HHFFFF) +Size 75: 38,75 GOOD (HHFFFF) +Size 76: 38,76 GOOD (HHFFFF) +Size 77: 39,77 GOOD (HHFFFF) +Size 78: 39,78 GOOD (HHFFFF) +Size 79: 40,79 GOOD (HHFFFF) +Size 80: 40,80 GOOD (HHFFFF) +Size 81: 41,81 GOOD (HHFFFF) +Size 82: 41,82 GOOD (HHFFFF) +Size 83: 42,83 GOOD (HHFFFF) +Size 84: 42,84 GOOD (HHFFFF) +Size 85: 43,85 GOOD (HHFFFF) +Size 86: 43,86 GOOD (HHFFFF) +Size 87: 44,87 GOOD (HHFFFF) +Size 88: 44,88 GOOD (HHFFFF) +Size 89: 45,89 GOOD (HHFFFF) +Size 90: 45,90 GOOD (HHFFFF) +Size 91: 46,91 GOOD (HHFFFF) +Size 92: 46,92 GOOD (HHFFFF) +Size 93: 47,93 GOOD (HHFFFF) +Size 94: 47,94 GOOD (HHFFFF) +Size 95: 48,95 GOOD (HHFFFF) +Size 96: 48,96 GOOD (HHFFFF) +Size 97: 49,97 GOOD (HHFFFF) +Size 98: 49,98 GOOD (HHFFFF) +Size 99: 50,99 GOOD (HHFFFF) +Size 100: 50,100 GOOD (HHFFFF) diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt new file mode 100644 index 0000000000..2f0ea1e7c2 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt @@ -0,0 +1,630 @@ +===================================== +Code Page 949, Korean, GulimChe font +===================================== + +Options: -face-gulimche -family 0x36 +Chars: A2 A3 2014 3044 30FC 4000 + +Vista +----- + +Size 1: 1,2 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 BAD (FFFFHH) +Size 4: 2,5 OK (HHHFFF) +Size 5: 3,6 BAD (HHHFHH) +Size 6: 3,7 OK (HHHFFF) +Size 7: 4,8 BAD (HHHFHH) +Size 8: 4,9 OK (HHHFFF) +Size 9: 5,10 BAD (HHHFHH) +Size 10: 5,11 OK (HHHFFF) +Size 11: 6,13 BAD (HHHFHH) +Size 12: 6,14 OK (HHHFFF) +Size 13: 7,15 BAD (HHHFHH) +Size 14: 7,16 OK (HHHFFF) +Size 15: 8,17 BAD (HHHFHH) +Size 16: 8,18 OK (HHHFFF) +Size 17: 9,20 BAD (HHHFHH) +Size 18: 9,21 OK (HHHFFF) +Size 19: 10,22 BAD (HHHFHH) +Size 20: 10,23 OK (HHHFFF) +Size 21: 11,24 BAD (HHHFHH) +Size 22: 11,25 OK (HHHFFF) +Size 23: 12,26 BAD (HHHFHH) +Size 24: 12,28 OK (HHHFFF) +Size 25: 13,29 BAD (HHHFHH) +Size 26: 13,30 OK (HHHFFF) +Size 27: 14,31 BAD (HHHFHH) +Size 28: 14,32 OK (HHHFFF) +Size 29: 15,33 BAD (HHHFHH) +Size 30: 15,34 OK (HHHFFF) +Size 31: 16,36 BAD (HHHFHH) +Size 32: 16,37 OK (HHHFFF) +Size 33: 17,38 BAD (HHHFHH) +Size 34: 17,39 OK (HHHFFF) +Size 35: 18,40 BAD (HHHFHH) +Size 36: 18,41 OK (HHHFFF) +Size 37: 19,42 BAD (HHHFHH) +Size 38: 19,44 OK (HHHFFF) +Size 39: 20,45 BAD (HHHFHH) +Size 40: 20,46 OK (HHHFFF) +Size 41: 21,47 BAD (HHHFHH) +Size 42: 21,48 OK (HHHFFF) +Size 43: 22,49 BAD (HHHFHH) +Size 44: 22,51 OK (HHHFFF) +Size 45: 23,52 BAD (HHHFHH) +Size 46: 23,53 OK (HHHFFF) +Size 47: 24,54 BAD (HHHFHH) +Size 48: 24,55 OK (HHHFFF) +Size 49: 25,56 BAD (HHHFHH) +Size 50: 25,57 OK (HHHFFF) +Size 51: 26,59 BAD (HHHFHH) +Size 52: 26,60 OK (HHHFFF) +Size 53: 27,61 BAD (HHHFHH) +Size 54: 27,62 OK (HHHFFF) +Size 55: 28,63 BAD (HHHFHH) +Size 56: 28,64 OK (HHHFFF) +Size 57: 29,65 BAD (HHHFHH) +Size 58: 29,67 OK (HHHFFF) +Size 59: 30,68 BAD (HHHFHH) +Size 60: 30,69 OK (HHHFFF) +Size 61: 31,70 BAD (HHHFHH) +Size 62: 31,71 OK (HHHFFF) +Size 63: 32,72 BAD (HHHFHH) +Size 64: 32,74 OK (HHHFFF) +Size 65: 33,75 BAD (HHHFHH) +Size 66: 33,76 OK (HHHFFF) +Size 67: 34,77 BAD (HHHFHH) +Size 68: 34,78 OK (HHHFFF) +Size 69: 35,79 BAD (HHHFHH) +Size 70: 35,80 OK (HHHFFF) +Size 71: 36,82 BAD (HHHFHH) +Size 72: 36,83 OK (HHHFFF) +Size 73: 37,84 BAD (HHHFHH) +Size 74: 37,85 OK (HHHFFF) +Size 75: 38,86 BAD (HHHFHH) +Size 76: 38,87 OK (HHHFFF) +Size 77: 39,88 BAD (HHHFHH) +Size 78: 39,90 OK (HHHFFF) +Size 79: 40,91 BAD (HHHFHH) +Size 80: 40,92 OK (HHHFFF) +Size 81: 41,93 BAD (HHHFHH) +Size 82: 41,94 OK (HHHFFF) +Size 83: 42,95 BAD (HHHFHH) +Size 84: 42,96 OK (HHHFFF) +Size 85: 43,98 BAD (HHHFHH) +Size 86: 43,99 OK (HHHFFF) +Size 87: 44,100 BAD (HHHFHH) +Size 88: 44,101 OK (HHHFFF) +Size 89: 45,102 BAD (HHHFHH) +Size 90: 45,103 OK (HHHFFF) +Size 91: 46,105 BAD (HHHFHH) +Size 92: 46,106 OK (HHHFFF) +Size 93: 47,107 BAD (HHHFHH) +Size 94: 47,108 OK (HHHFFF) +Size 95: 48,109 BAD (HHHFHH) +Size 96: 48,110 OK (HHHFFF) +Size 97: 49,111 BAD (HHHFHH) +Size 98: 49,113 OK (HHHFFF) +Size 99: 50,114 BAD (HHHFHH) +Size 100: 50,115 OK (HHHFFF) + +Windows 7 +--------- + +Size 1: 1,2 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 BAD (FFFFHH) +Size 4: 2,5 OK (HHHFFF) +Size 5: 3,6 BAD (FFFFHH) +Size 6: 3,7 OK (HHHFFF) +Size 7: 4,8 BAD (FFFFHH) +Size 8: 4,9 OK (HHHFFF) +Size 9: 5,10 BAD (FFFFHH) +Size 10: 5,11 OK (HHHFFF) +Size 11: 6,13 BAD (FFFFHH) +Size 12: 6,14 OK (HHHFFF) +Size 13: 7,15 BAD (FFFFHH) +Size 14: 7,16 OK (HHHFFF) +Size 15: 8,17 BAD (FFFFHH) +Size 16: 8,18 OK (HHHFFF) +Size 17: 9,20 BAD (FFFFHH) +Size 18: 9,21 OK (HHHFFF) +Size 19: 10,22 BAD (FFFFHH) +Size 20: 10,23 OK (HHHFFF) +Size 21: 11,24 BAD (FFFFHH) +Size 22: 11,25 OK (HHHFFF) +Size 23: 12,26 BAD (FFFFHH) +Size 24: 12,28 OK (HHHFFF) +Size 25: 13,29 BAD (FFFFHH) +Size 26: 13,30 OK (HHHFFF) +Size 27: 14,31 BAD (FFFFHH) +Size 28: 14,32 OK (HHHFFF) +Size 29: 15,33 BAD (FFFFHH) +Size 30: 15,34 OK (HHHFFF) +Size 31: 16,36 BAD (FFFFHH) +Size 32: 16,37 OK (HHHFFF) +Size 33: 17,38 BAD (FFFFHH) +Size 34: 17,39 OK (HHHFFF) +Size 35: 18,40 BAD (FFFFHH) +Size 36: 18,41 OK (HHHFFF) +Size 37: 19,42 BAD (FFFFHH) +Size 38: 19,44 OK (HHHFFF) +Size 39: 20,45 BAD (FFFFHH) +Size 40: 20,46 OK (HHHFFF) +Size 41: 21,47 BAD (FFFFHH) +Size 42: 21,48 OK (HHHFFF) +Size 43: 22,49 BAD (FFFFHH) +Size 44: 22,51 OK (HHHFFF) +Size 45: 23,52 BAD (FFFFHH) +Size 46: 23,53 OK (HHHFFF) +Size 47: 24,54 BAD (FFFFHH) +Size 48: 24,55 OK (HHHFFF) +Size 49: 25,56 BAD (FFFFHH) +Size 50: 25,57 OK (HHHFFF) +Size 51: 26,59 BAD (FFFFHH) +Size 52: 26,60 OK (HHHFFF) +Size 53: 27,61 BAD (FFFFHH) +Size 54: 27,62 OK (HHHFFF) +Size 55: 28,63 BAD (FFFFHH) +Size 56: 28,64 OK (HHHFFF) +Size 57: 29,65 BAD (FFFFHH) +Size 58: 29,67 OK (HHHFFF) +Size 59: 30,68 BAD (FFFFHH) +Size 60: 30,69 OK (HHHFFF) +Size 61: 31,70 BAD (FFFFHH) +Size 62: 31,71 OK (HHHFFF) +Size 63: 32,72 BAD (FFFFHH) +Size 64: 32,74 OK (HHHFFF) +Size 65: 33,75 BAD (FFFFHH) +Size 66: 33,76 OK (HHHFFF) +Size 67: 34,77 BAD (FFFFHH) +Size 68: 34,78 OK (HHHFFF) +Size 69: 35,79 BAD (FFFFHH) +Size 70: 35,80 OK (HHHFFF) +Size 71: 36,82 BAD (FFFFHH) +Size 72: 36,83 OK (HHHFFF) +Size 73: 37,84 BAD (FFFFHH) +Size 74: 37,85 OK (HHHFFF) +Size 75: 38,86 BAD (FFFFHH) +Size 76: 38,87 OK (HHHFFF) +Size 77: 39,88 BAD (FFFFHH) +Size 78: 39,90 OK (HHHFFF) +Size 79: 40,91 BAD (FFFFHH) +Size 80: 40,92 OK (HHHFFF) +Size 81: 41,93 BAD (FFFFHH) +Size 82: 41,94 OK (HHHFFF) +Size 83: 42,95 BAD (FFFFHH) +Size 84: 42,96 OK (HHHFFF) +Size 85: 43,98 BAD (FFFFHH) +Size 86: 43,99 OK (HHHFFF) +Size 87: 44,100 BAD (FFFFHH) +Size 88: 44,101 OK (HHHFFF) +Size 89: 45,102 BAD (FFFFHH) +Size 90: 45,103 OK (HHHFFF) +Size 91: 46,105 BAD (FFFFHH) +Size 92: 46,106 OK (HHHFFF) +Size 93: 47,107 BAD (FFFFHH) +Size 94: 47,108 OK (HHHFFF) +Size 95: 48,109 BAD (FFFFHH) +Size 96: 48,110 OK (HHHFFF) +Size 97: 49,111 BAD (FFFFHH) +Size 98: 49,113 OK (HHHFFF) +Size 99: 50,114 BAD (FFFFHH) +Size 100: 50,115 OK (HHHFFF) + +Windows 8 +--------- + +Size 1: 1,2 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 BAD (FFFFHH) +Size 4: 2,5 OK (HHHFFF) +Size 5: 3,6 BAD (FFFFHH) +Size 6: 3,7 OK (HHHFFF) +Size 7: 4,8 BAD (FFFFHH) +Size 8: 4,9 OK (HHHFFF) +Size 9: 5,10 BAD (FFFFHH) +Size 10: 5,11 OK (HHHFFF) +Size 11: 6,13 BAD (FFFFHH) +Size 12: 6,14 OK (HHHFFF) +Size 13: 7,15 BAD (FFFFHH) +Size 14: 7,16 OK (HHHFFF) +Size 15: 8,17 BAD (FFFFHH) +Size 16: 8,18 OK (HHHFFF) +Size 17: 9,20 BAD (FFFFHH) +Size 18: 9,21 OK (HHHFFF) +Size 19: 10,22 BAD (FFFFHH) +Size 20: 10,23 OK (HHHFFF) +Size 21: 11,24 BAD (FFFFHH) +Size 22: 11,25 OK (HHHFFF) +Size 23: 12,26 BAD (FFFFHH) +Size 24: 12,28 OK (HHHFFF) +Size 25: 13,29 BAD (FFFFHH) +Size 26: 13,30 OK (HHHFFF) +Size 27: 14,31 BAD (FFFFHH) +Size 28: 14,32 OK (HHHFFF) +Size 29: 15,33 BAD (FFFFHH) +Size 30: 15,34 OK (HHHFFF) +Size 31: 16,36 BAD (FFFFHH) +Size 32: 16,37 OK (HHHFFF) +Size 33: 17,38 BAD (FFFFHH) +Size 34: 17,39 OK (HHHFFF) +Size 35: 18,40 BAD (FFFFHH) +Size 36: 18,41 OK (HHHFFF) +Size 37: 19,42 BAD (FFFFHH) +Size 38: 19,44 OK (HHHFFF) +Size 39: 20,45 BAD (FFFFHH) +Size 40: 20,46 OK (HHHFFF) +Size 41: 21,47 BAD (FFFFHH) +Size 42: 21,48 OK (HHHFFF) +Size 43: 22,49 BAD (FFFFHH) +Size 44: 22,51 OK (HHHFFF) +Size 45: 23,52 BAD (FFFFHH) +Size 46: 23,53 OK (HHHFFF) +Size 47: 24,54 BAD (FFFFHH) +Size 48: 24,55 OK (HHHFFF) +Size 49: 25,56 BAD (FFFFHH) +Size 50: 25,57 OK (HHHFFF) +Size 51: 26,59 BAD (FFFFHH) +Size 52: 26,60 OK (HHHFFF) +Size 53: 27,61 BAD (FFFFHH) +Size 54: 27,62 OK (HHHFFF) +Size 55: 28,63 BAD (FFFFHH) +Size 56: 28,64 OK (HHHFFF) +Size 57: 29,65 BAD (FFFFHH) +Size 58: 29,67 OK (HHHFFF) +Size 59: 30,68 BAD (FFFFHH) +Size 60: 30,69 OK (HHHFFF) +Size 61: 31,70 BAD (FFFFHH) +Size 62: 31,71 OK (HHHFFF) +Size 63: 32,72 BAD (FFFFHH) +Size 64: 32,74 OK (HHHFFF) +Size 65: 33,75 BAD (FFFFHH) +Size 66: 33,76 OK (HHHFFF) +Size 67: 34,77 BAD (FFFFHH) +Size 68: 34,78 OK (HHHFFF) +Size 69: 35,79 BAD (FFFFHH) +Size 70: 35,80 OK (HHHFFF) +Size 71: 36,82 BAD (FFFFHH) +Size 72: 36,83 OK (HHHFFF) +Size 73: 37,84 BAD (FFFFHH) +Size 74: 37,85 OK (HHHFFF) +Size 75: 38,86 BAD (FFFFHH) +Size 76: 38,87 OK (HHHFFF) +Size 77: 39,88 BAD (FFFFHH) +Size 78: 39,90 OK (HHHFFF) +Size 79: 40,91 BAD (FFFFHH) +Size 80: 40,92 OK (HHHFFF) +Size 81: 41,93 BAD (FFFFHH) +Size 82: 41,94 OK (HHHFFF) +Size 83: 42,95 BAD (FFFFHH) +Size 84: 42,96 OK (HHHFFF) +Size 85: 43,98 BAD (FFFFHH) +Size 86: 43,99 OK (HHHFFF) +Size 87: 44,100 BAD (FFFFHH) +Size 88: 44,101 OK (HHHFFF) +Size 89: 45,102 BAD (FFFFHH) +Size 90: 45,103 OK (HHHFFF) +Size 91: 46,105 BAD (FFFFHH) +Size 92: 46,106 OK (HHHFFF) +Size 93: 47,107 BAD (FFFFHH) +Size 94: 47,108 OK (HHHFFF) +Size 95: 48,109 BAD (FFFFHH) +Size 96: 48,110 OK (HHHFFF) +Size 97: 49,111 BAD (FFFFHH) +Size 98: 49,113 OK (HHHFFF) +Size 99: 50,114 BAD (FFFFHH) +Size 100: 50,115 OK (HHHFFF) + +Windows 8.1 +----------- + +Size 1: 1,2 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 BAD (FFFFHH) +Size 4: 2,5 OK (HHHFFF) +Size 5: 3,6 BAD (FFFFHH) +Size 6: 3,7 OK (HHHFFF) +Size 7: 4,8 BAD (FFFFHH) +Size 8: 4,9 OK (HHHFFF) +Size 9: 5,10 BAD (FFFFHH) +Size 10: 5,11 OK (HHHFFF) +Size 11: 6,13 BAD (FFFFHH) +Size 12: 6,14 OK (HHHFFF) +Size 13: 7,15 BAD (FFFFHH) +Size 14: 7,16 OK (HHHFFF) +Size 15: 8,17 BAD (FFFFHH) +Size 16: 8,18 OK (HHHFFF) +Size 17: 9,20 BAD (FFFFHH) +Size 18: 9,21 OK (HHHFFF) +Size 19: 10,22 BAD (FFFFHH) +Size 20: 10,23 OK (HHHFFF) +Size 21: 11,24 BAD (FFFFHH) +Size 22: 11,25 OK (HHHFFF) +Size 23: 12,26 BAD (FFFFHH) +Size 24: 12,28 OK (HHHFFF) +Size 25: 13,29 BAD (FFFFHH) +Size 26: 13,30 OK (HHHFFF) +Size 27: 14,31 BAD (FFFFHH) +Size 28: 14,32 OK (HHHFFF) +Size 29: 15,33 BAD (FFFFHH) +Size 30: 15,34 OK (HHHFFF) +Size 31: 16,36 BAD (FFFFHH) +Size 32: 16,37 OK (HHHFFF) +Size 33: 17,38 BAD (FFFFHH) +Size 34: 17,39 OK (HHHFFF) +Size 35: 18,40 BAD (FFFFHH) +Size 36: 18,41 OK (HHHFFF) +Size 37: 19,42 BAD (FFFFHH) +Size 38: 19,44 OK (HHHFFF) +Size 39: 20,45 BAD (FFFFHH) +Size 40: 20,46 OK (HHHFFF) +Size 41: 21,47 BAD (FFFFHH) +Size 42: 21,48 OK (HHHFFF) +Size 43: 22,49 BAD (FFFFHH) +Size 44: 22,51 OK (HHHFFF) +Size 45: 23,52 BAD (FFFFHH) +Size 46: 23,53 OK (HHHFFF) +Size 47: 24,54 BAD (FFFFHH) +Size 48: 24,55 OK (HHHFFF) +Size 49: 25,56 BAD (FFFFHH) +Size 50: 25,57 OK (HHHFFF) +Size 51: 26,59 BAD (FFFFHH) +Size 52: 26,60 OK (HHHFFF) +Size 53: 27,61 BAD (FFFFHH) +Size 54: 27,62 OK (HHHFFF) +Size 55: 28,63 BAD (FFFFHH) +Size 56: 28,64 OK (HHHFFF) +Size 57: 29,65 BAD (FFFFHH) +Size 58: 29,67 OK (HHHFFF) +Size 59: 30,68 BAD (FFFFHH) +Size 60: 30,69 OK (HHHFFF) +Size 61: 31,70 BAD (FFFFHH) +Size 62: 31,71 OK (HHHFFF) +Size 63: 32,72 BAD (FFFFHH) +Size 64: 32,74 OK (HHHFFF) +Size 65: 33,75 BAD (FFFFHH) +Size 66: 33,76 OK (HHHFFF) +Size 67: 34,77 BAD (FFFFHH) +Size 68: 34,78 OK (HHHFFF) +Size 69: 35,79 BAD (FFFFHH) +Size 70: 35,80 OK (HHHFFF) +Size 71: 36,82 BAD (FFFFHH) +Size 72: 36,83 OK (HHHFFF) +Size 73: 37,84 BAD (FFFFHH) +Size 74: 37,85 OK (HHHFFF) +Size 75: 38,86 BAD (FFFFHH) +Size 76: 38,87 OK (HHHFFF) +Size 77: 39,88 BAD (FFFFHH) +Size 78: 39,90 OK (HHHFFF) +Size 79: 40,91 BAD (FFFFHH) +Size 80: 40,92 OK (HHHFFF) +Size 81: 41,93 BAD (FFFFHH) +Size 82: 41,94 OK (HHHFFF) +Size 83: 42,95 BAD (FFFFHH) +Size 84: 42,96 OK (HHHFFF) +Size 85: 43,98 BAD (FFFFHH) +Size 86: 43,99 OK (HHHFFF) +Size 87: 44,100 BAD (FFFFHH) +Size 88: 44,101 OK (HHHFFF) +Size 89: 45,102 BAD (FFFFHH) +Size 90: 45,103 OK (HHHFFF) +Size 91: 46,105 BAD (FFFFHH) +Size 92: 46,106 OK (HHHFFF) +Size 93: 47,107 BAD (FFFFHH) +Size 94: 47,108 OK (HHHFFF) +Size 95: 48,109 BAD (FFFFHH) +Size 96: 48,110 OK (HHHFFF) +Size 97: 49,111 BAD (FFFFHH) +Size 98: 49,113 OK (HHHFFF) +Size 99: 50,114 BAD (FFFFHH) +Size 100: 50,115 OK (HHHFFF) + +Windows 10 14342 Old Console +---------------------------- + +Size 1: 1,2 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 BAD (FFFFHH) +Size 4: 2,5 OK (HHHFFF) +Size 5: 3,6 BAD (FFFFHH) +Size 6: 3,7 OK (HHHFFF) +Size 7: 4,8 BAD (FFFFHH) +Size 8: 4,9 OK (HHHFFF) +Size 9: 5,10 BAD (FFFFHH) +Size 10: 5,11 OK (HHHFFF) +Size 11: 6,13 BAD (FFFFHH) +Size 12: 6,14 OK (HHHFFF) +Size 13: 7,15 BAD (FFFFHH) +Size 14: 7,16 OK (HHHFFF) +Size 15: 8,17 BAD (FFFFHH) +Size 16: 8,18 OK (HHHFFF) +Size 17: 9,20 BAD (FFFFHH) +Size 18: 9,21 OK (HHHFFF) +Size 19: 10,22 BAD (FFFFHH) +Size 20: 10,23 OK (HHHFFF) +Size 21: 11,24 BAD (FFFFHH) +Size 22: 11,25 OK (HHHFFF) +Size 23: 12,26 BAD (FFFFHH) +Size 24: 12,28 OK (HHHFFF) +Size 25: 13,29 BAD (FFFFHH) +Size 26: 13,30 OK (HHHFFF) +Size 27: 14,31 BAD (FFFFHH) +Size 28: 14,32 OK (HHHFFF) +Size 29: 15,33 BAD (FFFFHH) +Size 30: 15,34 OK (HHHFFF) +Size 31: 16,36 BAD (FFFFHH) +Size 32: 16,37 OK (HHHFFF) +Size 33: 17,38 BAD (FFFFHH) +Size 34: 17,39 OK (HHHFFF) +Size 35: 18,40 BAD (FFFFHH) +Size 36: 18,41 OK (HHHFFF) +Size 37: 19,42 BAD (FFFFHH) +Size 38: 19,44 OK (HHHFFF) +Size 39: 20,45 BAD (FFFFHH) +Size 40: 20,46 OK (HHHFFF) +Size 41: 21,47 BAD (FFFFHH) +Size 42: 21,48 OK (HHHFFF) +Size 43: 22,49 BAD (FFFFHH) +Size 44: 22,51 OK (HHHFFF) +Size 45: 23,52 BAD (FFFFHH) +Size 46: 23,53 OK (HHHFFF) +Size 47: 24,54 BAD (FFFFHH) +Size 48: 24,55 OK (HHHFFF) +Size 49: 25,56 BAD (FFFFHH) +Size 50: 25,57 OK (HHHFFF) +Size 51: 26,59 BAD (FFFFHH) +Size 52: 26,60 OK (HHHFFF) +Size 53: 27,61 BAD (FFFFHH) +Size 54: 27,62 OK (HHHFFF) +Size 55: 28,63 BAD (FFFFHH) +Size 56: 28,64 OK (HHHFFF) +Size 57: 29,65 BAD (FFFFHH) +Size 58: 29,67 OK (HHHFFF) +Size 59: 30,68 BAD (FFFFHH) +Size 60: 30,69 OK (HHHFFF) +Size 61: 31,70 BAD (FFFFHH) +Size 62: 31,71 OK (HHHFFF) +Size 63: 32,72 BAD (FFFFHH) +Size 64: 32,74 OK (HHHFFF) +Size 65: 33,75 BAD (FFFFHH) +Size 66: 33,76 OK (HHHFFF) +Size 67: 34,77 BAD (FFFFHH) +Size 68: 34,78 OK (HHHFFF) +Size 69: 35,79 BAD (FFFFHH) +Size 70: 35,80 OK (HHHFFF) +Size 71: 36,82 BAD (FFFFHH) +Size 72: 36,83 OK (HHHFFF) +Size 73: 37,84 BAD (FFFFHH) +Size 74: 37,85 OK (HHHFFF) +Size 75: 38,86 BAD (FFFFHH) +Size 76: 38,87 OK (HHHFFF) +Size 77: 39,88 BAD (FFFFHH) +Size 78: 39,90 OK (HHHFFF) +Size 79: 40,91 BAD (FFFFHH) +Size 80: 40,92 OK (HHHFFF) +Size 81: 41,93 BAD (FFFFHH) +Size 82: 41,94 OK (HHHFFF) +Size 83: 42,95 BAD (FFFFHH) +Size 84: 42,96 OK (HHHFFF) +Size 85: 43,98 BAD (FFFFHH) +Size 86: 43,99 OK (HHHFFF) +Size 87: 44,100 BAD (FFFFHH) +Size 88: 44,101 OK (HHHFFF) +Size 89: 45,102 BAD (FFFFHH) +Size 90: 45,103 OK (HHHFFF) +Size 91: 46,105 BAD (FFFFHH) +Size 92: 46,106 OK (HHHFFF) +Size 93: 47,107 BAD (FFFFHH) +Size 94: 47,108 OK (HHHFFF) +Size 95: 48,109 BAD (FFFFHH) +Size 96: 48,110 OK (HHHFFF) +Size 97: 49,111 BAD (FFFFHH) +Size 98: 49,113 OK (HHHFFF) +Size 99: 50,114 BAD (FFFFHH) +Size 100: 50,115 OK (HHHFFF) + +Windows 10 14342 New Console +---------------------------- + +Size 1: 1,1 OK (HHHFFF) +Size 2: 1,2 OK (HHHFFF) +Size 3: 2,3 OK (HHHFFF) +Size 4: 2,4 OK (HHHFFF) +Size 5: 3,5 OK (HHHFFF) +Size 6: 3,6 OK (HHHFFF) +Size 7: 4,7 OK (HHHFFF) +Size 8: 4,8 OK (HHHFFF) +Size 9: 5,9 OK (HHHFFF) +Size 10: 5,10 OK (HHHFFF) +Size 11: 6,11 OK (HHHFFF) +Size 12: 6,12 OK (HHHFFF) +Size 13: 7,13 OK (HHHFFF) +Size 14: 7,14 OK (HHHFFF) +Size 15: 8,15 OK (HHHFFF) +Size 16: 8,16 OK (HHHFFF) +Size 17: 9,17 OK (HHHFFF) +Size 18: 9,18 OK (HHHFFF) +Size 19: 10,19 OK (HHHFFF) +Size 20: 10,20 OK (HHHFFF) +Size 21: 11,21 OK (HHHFFF) +Size 22: 11,22 OK (HHHFFF) +Size 23: 12,23 OK (HHHFFF) +Size 24: 12,24 OK (HHHFFF) +Size 25: 13,25 OK (HHHFFF) +Size 26: 13,26 OK (HHHFFF) +Size 27: 14,27 OK (HHHFFF) +Size 28: 14,28 OK (HHHFFF) +Size 29: 15,29 OK (HHHFFF) +Size 30: 15,30 OK (HHHFFF) +Size 31: 16,31 OK (HHHFFF) +Size 32: 16,32 OK (HHHFFF) +Size 33: 17,33 OK (HHHFFF) +Size 34: 17,34 OK (HHHFFF) +Size 35: 18,35 OK (HHHFFF) +Size 36: 18,36 OK (HHHFFF) +Size 37: 19,37 OK (HHHFFF) +Size 38: 19,38 OK (HHHFFF) +Size 39: 20,39 OK (HHHFFF) +Size 40: 20,40 OK (HHHFFF) +Size 41: 21,41 OK (HHHFFF) +Size 42: 21,42 OK (HHHFFF) +Size 43: 22,43 OK (HHHFFF) +Size 44: 22,44 OK (HHHFFF) +Size 45: 23,45 OK (HHHFFF) +Size 46: 23,46 OK (HHHFFF) +Size 47: 24,47 OK (HHHFFF) +Size 48: 24,48 OK (HHHFFF) +Size 49: 25,49 OK (HHHFFF) +Size 50: 25,50 OK (HHHFFF) +Size 51: 26,51 OK (HHHFFF) +Size 52: 26,52 OK (HHHFFF) +Size 53: 27,53 OK (HHHFFF) +Size 54: 27,54 OK (HHHFFF) +Size 55: 28,55 OK (HHHFFF) +Size 56: 28,56 OK (HHHFFF) +Size 57: 29,57 OK (HHHFFF) +Size 58: 29,58 OK (HHHFFF) +Size 59: 30,59 OK (HHHFFF) +Size 60: 30,60 OK (HHHFFF) +Size 61: 31,61 OK (HHHFFF) +Size 62: 31,62 OK (HHHFFF) +Size 63: 32,63 OK (HHHFFF) +Size 64: 32,64 OK (HHHFFF) +Size 65: 33,65 OK (HHHFFF) +Size 66: 33,66 OK (HHHFFF) +Size 67: 34,67 OK (HHHFFF) +Size 68: 34,68 OK (HHHFFF) +Size 69: 35,69 OK (HHHFFF) +Size 70: 35,70 OK (HHHFFF) +Size 71: 36,71 OK (HHHFFF) +Size 72: 36,72 OK (HHHFFF) +Size 73: 37,73 OK (HHHFFF) +Size 74: 37,74 OK (HHHFFF) +Size 75: 38,75 OK (HHHFFF) +Size 76: 38,76 OK (HHHFFF) +Size 77: 39,77 OK (HHHFFF) +Size 78: 39,78 OK (HHHFFF) +Size 79: 40,79 OK (HHHFFF) +Size 80: 40,80 OK (HHHFFF) +Size 81: 41,81 OK (HHHFFF) +Size 82: 41,82 OK (HHHFFF) +Size 83: 42,83 OK (HHHFFF) +Size 84: 42,84 OK (HHHFFF) +Size 85: 43,85 OK (HHHFFF) +Size 86: 43,86 OK (HHHFFF) +Size 87: 44,87 OK (HHHFFF) +Size 88: 44,88 OK (HHHFFF) +Size 89: 45,89 OK (HHHFFF) +Size 90: 45,90 OK (HHHFFF) +Size 91: 46,91 OK (HHHFFF) +Size 92: 46,92 OK (HHHFFF) +Size 93: 47,93 OK (HHHFFF) +Size 94: 47,94 OK (HHHFFF) +Size 95: 48,95 OK (HHHFFF) +Size 96: 48,96 OK (HHHFFF) +Size 97: 49,97 OK (HHHFFF) +Size 98: 49,98 OK (HHHFFF) +Size 99: 50,99 OK (HHHFFF) +Size 100: 50,100 OK (HHHFFF) diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt new file mode 100644 index 0000000000..0dbade504d --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt @@ -0,0 +1,630 @@ +=========================================================== +Code Page 950, Chinese Traditional (Taiwan), MingLight font +=========================================================== + +Options: -face-minglight -family 0x36 +Chars: A2 A3 2014 3044 30FC 4000 + +Vista +----- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,4 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (HHHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (HHHFHH) +Size 8: 4,10 GOOD (HHFFFF) +Size 9: 5,11 BAD (HHHFHH) +Size 10: 5,12 GOOD (HHFFFF) +Size 11: 6,13 BAD (HHHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,16 BAD (HHHFHH) +Size 14: 7,17 GOOD (HHFFFF) +Size 15: 8,18 BAD (HHHFHH) +Size 16: 8,19 GOOD (HHFFFF) +Size 17: 9,20 BAD (HHHFHH) +Size 18: 9,22 GOOD (HHFFFF) +Size 19: 10,23 BAD (HHHFHH) +Size 20: 10,24 GOOD (HHFFFF) +Size 21: 11,25 BAD (HHHFHH) +Size 22: 11,26 GOOD (HHFFFF) +Size 23: 12,28 BAD (HHHFHH) +Size 24: 12,29 GOOD (HHFFFF) +Size 25: 13,30 BAD (HHHFHH) +Size 26: 13,31 GOOD (HHFFFF) +Size 27: 14,32 BAD (HHHFHH) +Size 28: 14,34 GOOD (HHFFFF) +Size 29: 15,35 BAD (HHHFHH) +Size 30: 15,36 GOOD (HHFFFF) +Size 31: 16,37 BAD (HHHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,40 BAD (HHHFHH) +Size 34: 17,41 GOOD (HHFFFF) +Size 35: 18,42 BAD (HHHFHH) +Size 36: 18,43 GOOD (HHFFFF) +Size 37: 19,44 BAD (HHHFHH) +Size 38: 19,46 GOOD (HHFFFF) +Size 39: 20,47 BAD (HHHFHH) +Size 40: 20,48 GOOD (HHFFFF) +Size 41: 21,49 BAD (HHHFHH) +Size 42: 21,50 GOOD (HHFFFF) +Size 43: 22,52 BAD (HHHFHH) +Size 44: 22,53 GOOD (HHFFFF) +Size 45: 23,54 BAD (HHHFHH) +Size 46: 23,55 GOOD (HHFFFF) +Size 47: 24,56 BAD (HHHFHH) +Size 48: 24,58 GOOD (HHFFFF) +Size 49: 25,59 BAD (HHHFHH) +Size 50: 25,60 GOOD (HHFFFF) +Size 51: 26,61 BAD (HHHFHH) +Size 52: 26,62 GOOD (HHFFFF) +Size 53: 27,64 BAD (HHHFHH) +Size 54: 27,65 GOOD (HHFFFF) +Size 55: 28,66 BAD (HHHFHH) +Size 56: 28,67 GOOD (HHFFFF) +Size 57: 29,68 BAD (HHHFHH) +Size 58: 29,70 GOOD (HHFFFF) +Size 59: 30,71 BAD (HHHFHH) +Size 60: 30,72 GOOD (HHFFFF) +Size 61: 31,73 BAD (HHHFHH) +Size 62: 31,74 GOOD (HHFFFF) +Size 63: 32,76 BAD (HHHFHH) +Size 64: 32,77 GOOD (HHFFFF) +Size 65: 33,78 BAD (HHHFHH) +Size 66: 33,79 GOOD (HHFFFF) +Size 67: 34,80 BAD (HHHFHH) +Size 68: 34,82 GOOD (HHFFFF) +Size 69: 35,83 BAD (HHHFHH) +Size 70: 35,84 GOOD (HHFFFF) +Size 71: 36,85 BAD (HHHFHH) +Size 72: 36,86 GOOD (HHFFFF) +Size 73: 37,88 BAD (HHHFHH) +Size 74: 37,89 GOOD (HHFFFF) +Size 75: 38,90 BAD (HHHFHH) +Size 76: 38,91 GOOD (HHFFFF) +Size 77: 39,92 BAD (HHHFHH) +Size 78: 39,94 GOOD (HHFFFF) +Size 79: 40,95 BAD (HHHFHH) +Size 80: 40,96 GOOD (HHFFFF) +Size 81: 41,97 BAD (HHHFHH) +Size 82: 41,98 GOOD (HHFFFF) +Size 83: 42,100 BAD (HHHFHH) +Size 84: 42,101 GOOD (HHFFFF) +Size 85: 43,102 BAD (HHHFHH) +Size 86: 43,103 GOOD (HHFFFF) +Size 87: 44,104 BAD (HHHFHH) +Size 88: 44,106 GOOD (HHFFFF) +Size 89: 45,107 BAD (HHHFHH) +Size 90: 45,108 GOOD (HHFFFF) +Size 91: 46,109 BAD (HHHFHH) +Size 92: 46,110 GOOD (HHFFFF) +Size 93: 47,112 BAD (HHHFHH) +Size 94: 47,113 GOOD (HHFFFF) +Size 95: 48,114 BAD (HHHFHH) +Size 96: 48,115 GOOD (HHFFFF) +Size 97: 49,116 BAD (HHHFHH) +Size 98: 49,118 GOOD (HHFFFF) +Size 99: 50,119 BAD (HHHFHH) +Size 100: 50,120 GOOD (HHFFFF) + +Windows 7 +--------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,4 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,10 GOOD (HHFFFF) +Size 9: 5,11 BAD (FFHFHH) +Size 10: 5,12 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,16 BAD (FFHFHH) +Size 14: 7,17 GOOD (HHFFFF) +Size 15: 8,18 BAD (FFHFHH) +Size 16: 8,19 GOOD (HHFFFF) +Size 17: 9,20 BAD (FFHFHH) +Size 18: 9,22 GOOD (HHFFFF) +Size 19: 10,23 BAD (FFHFHH) +Size 20: 10,24 GOOD (HHFFFF) +Size 21: 11,25 BAD (FFHFHH) +Size 22: 11,26 GOOD (HHFFFF) +Size 23: 12,28 BAD (FFHFHH) +Size 24: 12,29 GOOD (HHFFFF) +Size 25: 13,30 BAD (FFHFHH) +Size 26: 13,31 GOOD (HHFFFF) +Size 27: 14,32 BAD (FFHFHH) +Size 28: 14,34 GOOD (HHFFFF) +Size 29: 15,35 BAD (FFHFHH) +Size 30: 15,36 GOOD (HHFFFF) +Size 31: 16,37 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,40 BAD (FFHFHH) +Size 34: 17,41 GOOD (HHFFFF) +Size 35: 18,42 BAD (FFHFHH) +Size 36: 18,43 GOOD (HHFFFF) +Size 37: 19,44 BAD (FFHFHH) +Size 38: 19,46 GOOD (HHFFFF) +Size 39: 20,47 BAD (FFHFHH) +Size 40: 20,48 GOOD (HHFFFF) +Size 41: 21,49 BAD (FFHFHH) +Size 42: 21,50 GOOD (HHFFFF) +Size 43: 22,52 BAD (FFHFHH) +Size 44: 22,53 GOOD (HHFFFF) +Size 45: 23,54 BAD (FFHFHH) +Size 46: 23,55 GOOD (HHFFFF) +Size 47: 24,56 BAD (FFHFHH) +Size 48: 24,58 GOOD (HHFFFF) +Size 49: 25,59 BAD (FFHFHH) +Size 50: 25,60 GOOD (HHFFFF) +Size 51: 26,61 BAD (FFHFHH) +Size 52: 26,62 GOOD (HHFFFF) +Size 53: 27,64 BAD (FFHFHH) +Size 54: 27,65 GOOD (HHFFFF) +Size 55: 28,66 BAD (FFHFHH) +Size 56: 28,67 GOOD (HHFFFF) +Size 57: 29,68 BAD (FFHFHH) +Size 58: 29,70 GOOD (HHFFFF) +Size 59: 30,71 BAD (FFHFHH) +Size 60: 30,72 GOOD (HHFFFF) +Size 61: 31,73 BAD (FFHFHH) +Size 62: 31,74 GOOD (HHFFFF) +Size 63: 32,76 BAD (FFHFHH) +Size 64: 32,77 GOOD (HHFFFF) +Size 65: 33,78 BAD (FFHFHH) +Size 66: 33,79 GOOD (HHFFFF) +Size 67: 34,80 BAD (FFHFHH) +Size 68: 34,82 GOOD (HHFFFF) +Size 69: 35,83 BAD (FFHFHH) +Size 70: 35,84 GOOD (HHFFFF) +Size 71: 36,85 BAD (FFHFHH) +Size 72: 36,86 GOOD (HHFFFF) +Size 73: 37,88 BAD (FFHFHH) +Size 74: 37,89 GOOD (HHFFFF) +Size 75: 38,90 BAD (FFHFHH) +Size 76: 38,91 GOOD (HHFFFF) +Size 77: 39,92 BAD (FFHFHH) +Size 78: 39,94 GOOD (HHFFFF) +Size 79: 40,95 BAD (FFHFHH) +Size 80: 40,96 GOOD (HHFFFF) +Size 81: 41,97 BAD (FFHFHH) +Size 82: 41,98 GOOD (HHFFFF) +Size 83: 42,100 BAD (FFHFHH) +Size 84: 42,101 GOOD (HHFFFF) +Size 85: 43,102 BAD (FFHFHH) +Size 86: 43,103 GOOD (HHFFFF) +Size 87: 44,104 BAD (FFHFHH) +Size 88: 44,106 GOOD (HHFFFF) +Size 89: 45,107 BAD (FFHFHH) +Size 90: 45,108 GOOD (HHFFFF) +Size 91: 46,109 BAD (FFHFHH) +Size 92: 46,110 GOOD (HHFFFF) +Size 93: 47,112 BAD (FFHFHH) +Size 94: 47,113 GOOD (HHFFFF) +Size 95: 48,114 BAD (FFHFHH) +Size 96: 48,115 GOOD (HHFFFF) +Size 97: 49,116 BAD (FFHFHH) +Size 98: 49,118 GOOD (HHFFFF) +Size 99: 50,119 BAD (FFHFHH) +Size 100: 50,120 GOOD (HHFFFF) + +Windows 8 +--------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,4 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,10 GOOD (HHFFFF) +Size 9: 5,11 BAD (FFHFHH) +Size 10: 5,12 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,16 BAD (FFHFHH) +Size 14: 7,17 GOOD (HHFFFF) +Size 15: 8,18 BAD (FFHFHH) +Size 16: 8,19 GOOD (HHFFFF) +Size 17: 9,20 BAD (FFHFHH) +Size 18: 9,22 GOOD (HHFFFF) +Size 19: 10,23 BAD (FFHFHH) +Size 20: 10,24 GOOD (HHFFFF) +Size 21: 11,25 BAD (FFHFHH) +Size 22: 11,26 GOOD (HHFFFF) +Size 23: 12,28 BAD (FFHFHH) +Size 24: 12,29 GOOD (HHFFFF) +Size 25: 13,30 BAD (FFHFHH) +Size 26: 13,31 GOOD (HHFFFF) +Size 27: 14,32 BAD (FFHFHH) +Size 28: 14,34 GOOD (HHFFFF) +Size 29: 15,35 BAD (FFHFHH) +Size 30: 15,36 GOOD (HHFFFF) +Size 31: 16,37 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,40 BAD (FFHFHH) +Size 34: 17,41 GOOD (HHFFFF) +Size 35: 18,42 BAD (FFHFHH) +Size 36: 18,43 GOOD (HHFFFF) +Size 37: 19,44 BAD (FFHFHH) +Size 38: 19,46 GOOD (HHFFFF) +Size 39: 20,47 BAD (FFHFHH) +Size 40: 20,48 GOOD (HHFFFF) +Size 41: 21,49 BAD (FFHFHH) +Size 42: 21,50 GOOD (HHFFFF) +Size 43: 22,52 BAD (FFHFHH) +Size 44: 22,53 GOOD (HHFFFF) +Size 45: 23,54 BAD (FFHFHH) +Size 46: 23,55 GOOD (HHFFFF) +Size 47: 24,56 BAD (FFHFHH) +Size 48: 24,58 GOOD (HHFFFF) +Size 49: 25,59 BAD (FFHFHH) +Size 50: 25,60 GOOD (HHFFFF) +Size 51: 26,61 BAD (FFHFHH) +Size 52: 26,62 GOOD (HHFFFF) +Size 53: 27,64 BAD (FFHFHH) +Size 54: 27,65 GOOD (HHFFFF) +Size 55: 28,66 BAD (FFHFHH) +Size 56: 28,67 GOOD (HHFFFF) +Size 57: 29,68 BAD (FFHFHH) +Size 58: 29,70 GOOD (HHFFFF) +Size 59: 30,71 BAD (FFHFHH) +Size 60: 30,72 GOOD (HHFFFF) +Size 61: 31,73 BAD (FFHFHH) +Size 62: 31,74 GOOD (HHFFFF) +Size 63: 32,76 BAD (FFHFHH) +Size 64: 32,77 GOOD (HHFFFF) +Size 65: 33,78 BAD (FFHFHH) +Size 66: 33,79 GOOD (HHFFFF) +Size 67: 34,80 BAD (FFHFHH) +Size 68: 34,82 GOOD (HHFFFF) +Size 69: 35,83 BAD (FFHFHH) +Size 70: 35,84 GOOD (HHFFFF) +Size 71: 36,85 BAD (FFHFHH) +Size 72: 36,86 GOOD (HHFFFF) +Size 73: 37,88 BAD (FFHFHH) +Size 74: 37,89 GOOD (HHFFFF) +Size 75: 38,90 BAD (FFHFHH) +Size 76: 38,91 GOOD (HHFFFF) +Size 77: 39,92 BAD (FFHFHH) +Size 78: 39,94 GOOD (HHFFFF) +Size 79: 40,95 BAD (FFHFHH) +Size 80: 40,96 GOOD (HHFFFF) +Size 81: 41,97 BAD (FFHFHH) +Size 82: 41,98 GOOD (HHFFFF) +Size 83: 42,100 BAD (FFHFHH) +Size 84: 42,101 GOOD (HHFFFF) +Size 85: 43,102 BAD (FFHFHH) +Size 86: 43,103 GOOD (HHFFFF) +Size 87: 44,104 BAD (FFHFHH) +Size 88: 44,106 GOOD (HHFFFF) +Size 89: 45,107 BAD (FFHFHH) +Size 90: 45,108 GOOD (HHFFFF) +Size 91: 46,109 BAD (FFHFHH) +Size 92: 46,110 GOOD (HHFFFF) +Size 93: 47,112 BAD (FFHFHH) +Size 94: 47,113 GOOD (HHFFFF) +Size 95: 48,114 BAD (FFHFHH) +Size 96: 48,115 GOOD (HHFFFF) +Size 97: 49,116 BAD (FFHFHH) +Size 98: 49,118 GOOD (HHFFFF) +Size 99: 50,119 BAD (FFHFHH) +Size 100: 50,120 GOOD (HHFFFF) + +Windows 8.1 +----------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,4 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,10 GOOD (HHFFFF) +Size 9: 5,11 BAD (FFHFHH) +Size 10: 5,12 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,16 BAD (FFHFHH) +Size 14: 7,17 GOOD (HHFFFF) +Size 15: 8,18 BAD (FFHFHH) +Size 16: 8,19 GOOD (HHFFFF) +Size 17: 9,20 BAD (FFHFHH) +Size 18: 9,22 GOOD (HHFFFF) +Size 19: 10,23 BAD (FFHFHH) +Size 20: 10,24 GOOD (HHFFFF) +Size 21: 11,25 BAD (FFHFHH) +Size 22: 11,26 GOOD (HHFFFF) +Size 23: 12,28 BAD (FFHFHH) +Size 24: 12,29 GOOD (HHFFFF) +Size 25: 13,30 BAD (FFHFHH) +Size 26: 13,31 GOOD (HHFFFF) +Size 27: 14,32 BAD (FFHFHH) +Size 28: 14,34 GOOD (HHFFFF) +Size 29: 15,35 BAD (FFHFHH) +Size 30: 15,36 GOOD (HHFFFF) +Size 31: 16,37 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,40 BAD (FFHFHH) +Size 34: 17,41 GOOD (HHFFFF) +Size 35: 18,42 BAD (FFHFHH) +Size 36: 18,43 GOOD (HHFFFF) +Size 37: 19,44 BAD (FFHFHH) +Size 38: 19,46 GOOD (HHFFFF) +Size 39: 20,47 BAD (FFHFHH) +Size 40: 20,48 GOOD (HHFFFF) +Size 41: 21,49 BAD (FFHFHH) +Size 42: 21,50 GOOD (HHFFFF) +Size 43: 22,52 BAD (FFHFHH) +Size 44: 22,53 GOOD (HHFFFF) +Size 45: 23,54 BAD (FFHFHH) +Size 46: 23,55 GOOD (HHFFFF) +Size 47: 24,56 BAD (FFHFHH) +Size 48: 24,58 GOOD (HHFFFF) +Size 49: 25,59 BAD (FFHFHH) +Size 50: 25,60 GOOD (HHFFFF) +Size 51: 26,61 BAD (FFHFHH) +Size 52: 26,62 GOOD (HHFFFF) +Size 53: 27,64 BAD (FFHFHH) +Size 54: 27,65 GOOD (HHFFFF) +Size 55: 28,66 BAD (FFHFHH) +Size 56: 28,67 GOOD (HHFFFF) +Size 57: 29,68 BAD (FFHFHH) +Size 58: 29,70 GOOD (HHFFFF) +Size 59: 30,71 BAD (FFHFHH) +Size 60: 30,72 GOOD (HHFFFF) +Size 61: 31,73 BAD (FFHFHH) +Size 62: 31,74 GOOD (HHFFFF) +Size 63: 32,76 BAD (FFHFHH) +Size 64: 32,77 GOOD (HHFFFF) +Size 65: 33,78 BAD (FFHFHH) +Size 66: 33,79 GOOD (HHFFFF) +Size 67: 34,80 BAD (FFHFHH) +Size 68: 34,82 GOOD (HHFFFF) +Size 69: 35,83 BAD (FFHFHH) +Size 70: 35,84 GOOD (HHFFFF) +Size 71: 36,85 BAD (FFHFHH) +Size 72: 36,86 GOOD (HHFFFF) +Size 73: 37,88 BAD (FFHFHH) +Size 74: 37,89 GOOD (HHFFFF) +Size 75: 38,90 BAD (FFHFHH) +Size 76: 38,91 GOOD (HHFFFF) +Size 77: 39,92 BAD (FFHFHH) +Size 78: 39,94 GOOD (HHFFFF) +Size 79: 40,95 BAD (FFHFHH) +Size 80: 40,96 GOOD (HHFFFF) +Size 81: 41,97 BAD (FFHFHH) +Size 82: 41,98 GOOD (HHFFFF) +Size 83: 42,100 BAD (FFHFHH) +Size 84: 42,101 GOOD (HHFFFF) +Size 85: 43,102 BAD (FFHFHH) +Size 86: 43,103 GOOD (HHFFFF) +Size 87: 44,104 BAD (FFHFHH) +Size 88: 44,106 GOOD (HHFFFF) +Size 89: 45,107 BAD (FFHFHH) +Size 90: 45,108 GOOD (HHFFFF) +Size 91: 46,109 BAD (FFHFHH) +Size 92: 46,110 GOOD (HHFFFF) +Size 93: 47,112 BAD (FFHFHH) +Size 94: 47,113 GOOD (HHFFFF) +Size 95: 48,114 BAD (FFHFHH) +Size 96: 48,115 GOOD (HHFFFF) +Size 97: 49,116 BAD (FFHFHH) +Size 98: 49,118 GOOD (HHFFFF) +Size 99: 50,119 BAD (FFHFHH) +Size 100: 50,120 GOOD (HHFFFF) + +Windows 10 14342 Old Console +---------------------------- + +Size 1: 1,2 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,4 BAD (FFHFHH) +Size 4: 2,5 GOOD (HHFFFF) +Size 5: 3,6 BAD (FFHFHH) +Size 6: 3,7 GOOD (HHFFFF) +Size 7: 4,8 BAD (FFHFHH) +Size 8: 4,10 GOOD (HHFFFF) +Size 9: 5,11 BAD (FFHFHH) +Size 10: 5,12 GOOD (HHFFFF) +Size 11: 6,13 BAD (FFHFHH) +Size 12: 6,14 GOOD (HHFFFF) +Size 13: 7,16 BAD (FFHFHH) +Size 14: 7,17 GOOD (HHFFFF) +Size 15: 8,18 BAD (FFHFHH) +Size 16: 8,19 GOOD (HHFFFF) +Size 17: 9,20 BAD (FFHFHH) +Size 18: 9,22 GOOD (HHFFFF) +Size 19: 10,23 BAD (FFHFHH) +Size 20: 10,24 GOOD (HHFFFF) +Size 21: 11,25 BAD (FFHFHH) +Size 22: 11,26 GOOD (HHFFFF) +Size 23: 12,28 BAD (FFHFHH) +Size 24: 12,29 GOOD (HHFFFF) +Size 25: 13,30 BAD (FFHFHH) +Size 26: 13,31 GOOD (HHFFFF) +Size 27: 14,32 BAD (FFHFHH) +Size 28: 14,34 GOOD (HHFFFF) +Size 29: 15,35 BAD (FFHFHH) +Size 30: 15,36 GOOD (HHFFFF) +Size 31: 16,37 BAD (FFHFHH) +Size 32: 16,38 GOOD (HHFFFF) +Size 33: 17,40 BAD (FFHFHH) +Size 34: 17,41 GOOD (HHFFFF) +Size 35: 18,42 BAD (FFHFHH) +Size 36: 18,43 GOOD (HHFFFF) +Size 37: 19,44 BAD (FFHFHH) +Size 38: 19,46 GOOD (HHFFFF) +Size 39: 20,47 BAD (FFHFHH) +Size 40: 20,48 GOOD (HHFFFF) +Size 41: 21,49 BAD (FFHFHH) +Size 42: 21,50 GOOD (HHFFFF) +Size 43: 22,52 BAD (FFHFHH) +Size 44: 22,53 GOOD (HHFFFF) +Size 45: 23,54 BAD (FFHFHH) +Size 46: 23,55 GOOD (HHFFFF) +Size 47: 24,56 BAD (FFHFHH) +Size 48: 24,58 GOOD (HHFFFF) +Size 49: 25,59 BAD (FFHFHH) +Size 50: 25,60 GOOD (HHFFFF) +Size 51: 26,61 BAD (FFHFHH) +Size 52: 26,62 GOOD (HHFFFF) +Size 53: 27,64 BAD (FFHFHH) +Size 54: 27,65 GOOD (HHFFFF) +Size 55: 28,66 BAD (FFHFHH) +Size 56: 28,67 GOOD (HHFFFF) +Size 57: 29,68 BAD (FFHFHH) +Size 58: 29,70 GOOD (HHFFFF) +Size 59: 30,71 BAD (FFHFHH) +Size 60: 30,72 GOOD (HHFFFF) +Size 61: 31,73 BAD (FFHFHH) +Size 62: 31,74 GOOD (HHFFFF) +Size 63: 32,76 BAD (FFHFHH) +Size 64: 32,77 GOOD (HHFFFF) +Size 65: 33,78 BAD (FFHFHH) +Size 66: 33,79 GOOD (HHFFFF) +Size 67: 34,80 BAD (FFHFHH) +Size 68: 34,82 GOOD (HHFFFF) +Size 69: 35,83 BAD (FFHFHH) +Size 70: 35,84 GOOD (HHFFFF) +Size 71: 36,85 BAD (FFHFHH) +Size 72: 36,86 GOOD (HHFFFF) +Size 73: 37,88 BAD (FFHFHH) +Size 74: 37,89 GOOD (HHFFFF) +Size 75: 38,90 BAD (FFHFHH) +Size 76: 38,91 GOOD (HHFFFF) +Size 77: 39,92 BAD (FFHFHH) +Size 78: 39,94 GOOD (HHFFFF) +Size 79: 40,95 BAD (FFHFHH) +Size 80: 40,96 GOOD (HHFFFF) +Size 81: 41,97 BAD (FFHFHH) +Size 82: 41,98 GOOD (HHFFFF) +Size 83: 42,100 BAD (FFHFHH) +Size 84: 42,101 GOOD (HHFFFF) +Size 85: 43,102 BAD (FFHFHH) +Size 86: 43,103 GOOD (HHFFFF) +Size 87: 44,104 BAD (FFHFHH) +Size 88: 44,106 GOOD (HHFFFF) +Size 89: 45,107 BAD (FFHFHH) +Size 90: 45,108 GOOD (HHFFFF) +Size 91: 46,109 BAD (FFHFHH) +Size 92: 46,110 GOOD (HHFFFF) +Size 93: 47,112 BAD (FFHFHH) +Size 94: 47,113 GOOD (HHFFFF) +Size 95: 48,114 BAD (FFHFHH) +Size 96: 48,115 GOOD (HHFFFF) +Size 97: 49,116 BAD (FFHFHH) +Size 98: 49,118 GOOD (HHFFFF) +Size 99: 50,119 BAD (FFHFHH) +Size 100: 50,120 GOOD (HHFFFF) + +Windows 10 14342 New Console +---------------------------- + +Size 1: 1,1 GOOD (HHFFFF) +Size 2: 1,2 GOOD (HHFFFF) +Size 3: 2,3 GOOD (HHFFFF) +Size 4: 2,4 GOOD (HHFFFF) +Size 5: 3,5 GOOD (HHFFFF) +Size 6: 3,6 GOOD (HHFFFF) +Size 7: 4,7 GOOD (HHFFFF) +Size 8: 4,8 GOOD (HHFFFF) +Size 9: 5,9 GOOD (HHFFFF) +Size 10: 5,10 GOOD (HHFFFF) +Size 11: 6,11 GOOD (HHFFFF) +Size 12: 6,12 GOOD (HHFFFF) +Size 13: 7,13 GOOD (HHFFFF) +Size 14: 7,14 GOOD (HHFFFF) +Size 15: 8,15 GOOD (HHFFFF) +Size 16: 8,16 GOOD (HHFFFF) +Size 17: 9,17 GOOD (HHFFFF) +Size 18: 9,18 GOOD (HHFFFF) +Size 19: 10,19 GOOD (HHFFFF) +Size 20: 10,20 GOOD (HHFFFF) +Size 21: 11,21 GOOD (HHFFFF) +Size 22: 11,22 GOOD (HHFFFF) +Size 23: 12,23 GOOD (HHFFFF) +Size 24: 12,24 GOOD (HHFFFF) +Size 25: 13,25 GOOD (HHFFFF) +Size 26: 13,26 GOOD (HHFFFF) +Size 27: 14,27 GOOD (HHFFFF) +Size 28: 14,28 GOOD (HHFFFF) +Size 29: 15,29 GOOD (HHFFFF) +Size 30: 15,30 GOOD (HHFFFF) +Size 31: 16,31 GOOD (HHFFFF) +Size 32: 16,32 GOOD (HHFFFF) +Size 33: 17,33 GOOD (HHFFFF) +Size 34: 17,34 GOOD (HHFFFF) +Size 35: 18,35 GOOD (HHFFFF) +Size 36: 18,36 GOOD (HHFFFF) +Size 37: 19,37 GOOD (HHFFFF) +Size 38: 19,38 GOOD (HHFFFF) +Size 39: 20,39 GOOD (HHFFFF) +Size 40: 20,40 GOOD (HHFFFF) +Size 41: 21,41 GOOD (HHFFFF) +Size 42: 21,42 GOOD (HHFFFF) +Size 43: 22,43 GOOD (HHFFFF) +Size 44: 22,44 GOOD (HHFFFF) +Size 45: 23,45 GOOD (HHFFFF) +Size 46: 23,46 GOOD (HHFFFF) +Size 47: 24,47 GOOD (HHFFFF) +Size 48: 24,48 GOOD (HHFFFF) +Size 49: 25,49 GOOD (HHFFFF) +Size 50: 25,50 GOOD (HHFFFF) +Size 51: 26,51 GOOD (HHFFFF) +Size 52: 26,52 GOOD (HHFFFF) +Size 53: 27,53 GOOD (HHFFFF) +Size 54: 27,54 GOOD (HHFFFF) +Size 55: 28,55 GOOD (HHFFFF) +Size 56: 28,56 GOOD (HHFFFF) +Size 57: 29,57 GOOD (HHFFFF) +Size 58: 29,58 GOOD (HHFFFF) +Size 59: 30,59 GOOD (HHFFFF) +Size 60: 30,60 GOOD (HHFFFF) +Size 61: 31,61 GOOD (HHFFFF) +Size 62: 31,62 GOOD (HHFFFF) +Size 63: 32,63 GOOD (HHFFFF) +Size 64: 32,64 GOOD (HHFFFF) +Size 65: 33,65 GOOD (HHFFFF) +Size 66: 33,66 GOOD (HHFFFF) +Size 67: 34,67 GOOD (HHFFFF) +Size 68: 34,68 GOOD (HHFFFF) +Size 69: 35,69 GOOD (HHFFFF) +Size 70: 35,70 GOOD (HHFFFF) +Size 71: 36,71 GOOD (HHFFFF) +Size 72: 36,72 GOOD (HHFFFF) +Size 73: 37,73 GOOD (HHFFFF) +Size 74: 37,74 GOOD (HHFFFF) +Size 75: 38,75 GOOD (HHFFFF) +Size 76: 38,76 GOOD (HHFFFF) +Size 77: 39,77 GOOD (HHFFFF) +Size 78: 39,78 GOOD (HHFFFF) +Size 79: 40,79 GOOD (HHFFFF) +Size 80: 40,80 GOOD (HHFFFF) +Size 81: 41,81 GOOD (HHFFFF) +Size 82: 41,82 GOOD (HHFFFF) +Size 83: 42,83 GOOD (HHFFFF) +Size 84: 42,84 GOOD (HHFFFF) +Size 85: 43,85 GOOD (HHFFFF) +Size 86: 43,86 GOOD (HHFFFF) +Size 87: 44,87 GOOD (HHFFFF) +Size 88: 44,88 GOOD (HHFFFF) +Size 89: 45,89 GOOD (HHFFFF) +Size 90: 45,90 GOOD (HHFFFF) +Size 91: 46,91 GOOD (HHFFFF) +Size 92: 46,92 GOOD (HHFFFF) +Size 93: 47,93 GOOD (HHFFFF) +Size 94: 47,94 GOOD (HHFFFF) +Size 95: 48,95 GOOD (HHFFFF) +Size 96: 48,96 GOOD (HHFFFF) +Size 97: 49,97 GOOD (HHFFFF) +Size 98: 49,98 GOOD (HHFFFF) +Size 99: 50,99 GOOD (HHFFFF) +Size 100: 50,100 GOOD (HHFFFF) diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt new file mode 100644 index 0000000000..d5261d8db3 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt @@ -0,0 +1,16 @@ +The narrowest allowed console window, in pixels, on a conventional (~96dpi) +monitor: + +(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 1 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12 + +(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 16 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12 + + sz1:px sz1:col sz16:px sz16:col +Vista: 124 104 137 10 +Windows 7: 132 112 147 11 +Windows 8: 140 120 147 11 +Windows 8.1: 140 120 147 11 +Windows 10 OLD: 136 116 147 11 +Windows 10 NEW: 136 103 136 10 + +I used build 14342 to test Windows 10. diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt new file mode 100644 index 0000000000..15a825cb51 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt @@ -0,0 +1,4 @@ +As before, avoid odd sizes in favor of even sizes. + +It's curious that the Japanese font is handled so poorly, especially with +Windows 8 and later. diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt new file mode 100644 index 0000000000..fef397a1e3 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt @@ -0,0 +1,144 @@ +Issues: + + - Starting with the 14342 build, changing the font using + SetCurrentConsoleFontEx does not affect the window size. e.g. The content + itself will resize/redraw, but the window neither shrinks nor expands. + Presumably this is an oversight? It's almost a convenience; if a program + is going to resize the window anyway, then it's nice that the window size + contraints don't get in the way. Ordinarily, changing the font doesn't just + change the window size in pixels--it can also change the size as measured in + rows and columns. + + - (Aside: in the 14342 build, there is also a bug with wmic.exe. Open a console + with more than 300 lines of screen buffer, then fill those lines with, e.g., + dir /s. Then run wmic.exe. You won't be able to see the wmic.exe prompt. + If you query the screen buffer info somehow, you'll notice that the srWindow + is not contained within the dwSize. This breaks winpty's scraping, because + it's invalid.) + + - In build 14316, with the Japanese locale, with the 437 code page, attempting + to set the Consolas font instead sets the Terminal (raster) font. It seems + to pick an appropriate vertical size. + + - It seems necessary to specify "-family 0x36" for maximum reliability. + Setting the family to 0 almost always works, and specifying just -tt rarely + works. + +Win7 + English locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 932 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt unreliable + SetFont.exe -face Consolas -h 16 -family 0x36 works + +Win10 Build 10586 + New console + Japanese locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + +Win10 Build 14316 + Old console + English locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 932 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selected very small Consolas font + SetFont.exe -face Consolas -h 16 -family 0x36 works + New console + English locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt works + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 932 code page: + SetFont.exe -face Consolas -h 16 selects gothic instead + SetFont.exe -face Consolas -h 16 -tt selects gothic instead + SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead + Japanese locale / 437 code page: + SetFont.exe -face Consolas -h 16 selects Terminal font instead + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36(*) selects Terminal font instead + +Win10 Build 14342 + Old Console + English locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 932 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead + SetFont.exe -face Consolas -h 16 -family 0x36 works + New console + English locale / 437 code page: + SetFont.exe -face Consolas -h 16 works + SetFont.exe -face Consolas -h 16 -tt works + SetFont.exe -face Consolas -h 16 -family 0x36 works + Japanese locale / 932 code page: + SetFont.exe -face Consolas -h 16 selects gothic instead + SetFont.exe -face Consolas -h 16 -tt selects gothic instead + SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead + Japanese locale / 437 code page: + SetFont.exe -face Consolas -h 16 selects Terminal font instead + SetFont.exe -face Consolas -h 16 -tt works + SetFont.exe -face Consolas -h 16 -family 0x36 works + +(*) I was trying to figure out whether the inconsistency was at when I stumbled +onto this completely unexpected bug. Here's more detail: + + F:\>SetFont.exe -face Consolas -h 16 -family 0x36 -weight normal -w 8 + Setting to: nFont=0 dwFontSize=(8,16) FontFamily=0x36 FontWeight=400 FaceName="Consolas" + SetCurrentConsoleFontEx returned 1 + + F:\>GetFont.exe + largestConsoleWindowSize=(96,50) + maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C) + maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C) + 00-00: 12x16 + GetNumberOfConsoleFonts returned 0 + CP=437 OutputCP=437 + + F:\>SetFont.exe -face "Lucida Console" -h 16 -family 0x36 -weight normal + Setting to: nFont=0 dwFontSize=(0,16) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console" + SetCurrentConsoleFontEx returned 1 + + F:\>GetFont.exe + largestConsoleWindowSize=(96,50) + maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C) + maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C) + 00-00: 12x16 + GetNumberOfConsoleFonts returned 0 + CP=437 OutputCP=437 + + F:\>SetFont.exe -face "Lucida Console" -h 12 -family 0x36 -weight normal + Setting to: nFont=0 dwFontSize=(0,12) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console" + SetCurrentConsoleFontEx returned 1 + + F:\>GetFont.exe + largestConsoleWindowSize=(230,66) + maxWnd=0: nFont=0 dwFontSize=(5,12) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C) + maxWnd=1: nFont=0 dwFontSize=(116,36) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C) + 00-00: 5x12 + GetNumberOfConsoleFonts returned 0 + CP=437 OutputCP=437 + +Even attempting to set to a Lucida Console / Consolas font from the Console +properties dialog fails. diff --git a/src/libs/3rdparty/winpty/misc/FontSurvey.cc b/src/libs/3rdparty/winpty/misc/FontSurvey.cc new file mode 100644 index 0000000000..254bcc81a6 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/FontSurvey.cc @@ -0,0 +1,100 @@ +#include <windows.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <vector> + +#include "TestUtil.cc" + +#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0])) + +// See https://en.wikipedia.org/wiki/List_of_CJK_fonts +const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese +const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese +const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese +const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean + +std::vector<bool> condense(const std::vector<CHAR_INFO> &buf) { + std::vector<bool> ret; + size_t i = 0; + while (i < buf.size()) { + if (buf[i].Char.UnicodeChar == L' ' && + ((buf[i].Attributes & 0x300) == 0)) { + // end of line + break; + } else if (i + 1 < buf.size() && + ((buf[i].Attributes & 0x300) == 0x100) && + ((buf[i + 1].Attributes & 0x300) == 0x200) && + buf[i].Char.UnicodeChar != L' ' && + buf[i].Char.UnicodeChar == buf[i + 1].Char.UnicodeChar) { + // double-width + ret.push_back(true); + i += 2; + } else if ((buf[i].Attributes & 0x300) == 0) { + // single-width + ret.push_back(false); + i++; + } else { + ASSERT(false && "unexpected output"); + } + } + return ret; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s \"arguments for SetFont.exe\"\n", argv[0]); + return 1; + } + + const char *setFontArgs = argv[1]; + + const wchar_t testLine[] = { 0xA2, 0xA3, 0x2014, 0x3044, 0x30FC, 0x4000, 0 }; + const HANDLE conout = openConout(); + + char setFontCmd[1024]; + for (int h = 1; h <= 100; ++h) { + sprintf(setFontCmd, ".\\SetFont.exe %s -h %d && cls", setFontArgs, h); + system(setFontCmd); + + CONSOLE_FONT_INFOEX infoex = {}; + infoex.cbSize = sizeof(infoex); + BOOL success = GetCurrentConsoleFontEx(conout, FALSE, &infoex); + ASSERT(success && "GetCurrentConsoleFontEx failed"); + + DWORD actual = 0; + success = WriteConsoleW(conout, testLine, wcslen(testLine), &actual, nullptr); + ASSERT(success && actual == wcslen(testLine)); + + std::vector<CHAR_INFO> readBuf(14); + const SMALL_RECT readRegion = {0, 0, static_cast<short>(readBuf.size() - 1), 0}; + SMALL_RECT readRegion2 = readRegion; + success = ReadConsoleOutputW( + conout, readBuf.data(), + {static_cast<short>(readBuf.size()), 1}, + {0, 0}, + &readRegion2); + ASSERT(success && !memcmp(&readRegion, &readRegion2, sizeof(readRegion))); + + const auto widths = condense(readBuf); + std::string widthsStr; + for (bool width : widths) { + widthsStr.append(width ? "F" : "H"); + } + char size[16]; + sprintf(size, "%d,%d", infoex.dwFontSize.X, infoex.dwFontSize.Y); + const char *status = ""; + if (widthsStr == "HHFFFF") { + status = "GOOD"; + } else if (widthsStr == "HHHFFF") { + status = "OK"; + } else { + status = "BAD"; + } + trace("Size %3d: %-7s %-4s (%s)", h, size, status, widthsStr.c_str()); + } + sprintf(setFontCmd, ".\\SetFont.exe %s -h 14", setFontArgs); + system(setFontCmd); +} diff --git a/src/libs/3rdparty/winpty/misc/FormatChar.h b/src/libs/3rdparty/winpty/misc/FormatChar.h new file mode 100644 index 0000000000..aade488f9e --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/FormatChar.h @@ -0,0 +1,21 @@ +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +static inline void formatChar(char *str, char ch) +{ + // Print some common control codes. + switch (ch) { + case '\r': strcpy(str, "CR "); break; + case '\n': strcpy(str, "LF "); break; + case ' ': strcpy(str, "SP "); break; + case 27: strcpy(str, "^[ "); break; + case 3: strcpy(str, "^C "); break; + default: + if (isgraph(ch)) + sprintf(str, "%c ", ch); + else + sprintf(str, "%02x ", ch); + break; + } +} diff --git a/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc b/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc new file mode 100644 index 0000000000..2c0b0086a1 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc @@ -0,0 +1,62 @@ +#include <windows.h> + +#include "TestUtil.cc" + +const int SC_CONSOLE_MARK = 0xFFF2; +const int SC_CONSOLE_SELECT_ALL = 0xFFF5; + +int main(int argc, char *argv[0]) { + + if (argc != 2) { + printf("Usage: %s (mark|selectall|read)\n", argv[0]); + return 1; + } + + enum class Test { Mark, SelectAll, Read } test; + if (!strcmp(argv[1], "mark")) { + test = Test::Mark; + } else if (!strcmp(argv[1], "selectall")) { + test = Test::SelectAll; + } else if (!strcmp(argv[1], "read")) { + test = Test::Read; + } else { + printf("Invalid test: %s\n", argv[1]); + return 1; + } + + HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + TimeMeasurement tm; + HWND hwnd = GetConsoleWindow(); + + setWindowPos(0, 0, 1, 1); + setBufferSize(100, 3000); + system("cls"); + setWindowPos(0, 2975, 100, 25); + setCursorPos(0, 2999); + + ShowWindow(hwnd, SW_HIDE); + + for (int i = 0; i < 1000; ++i) { + // CONSOLE_SCREEN_BUFFER_INFO info = {}; + // GetConsoleScreenBufferInfo(conout, &info); + + if (test == Test::Mark) { + SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0); + SendMessage(hwnd, WM_CHAR, 27, 0x00010001); + } else if (test == Test::SelectAll) { + SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0); + SendMessage(hwnd, WM_CHAR, 27, 0x00010001); + } else if (test == Test::Read) { + static CHAR_INFO buffer[100 * 3000]; + const SMALL_RECT readRegion = {0, 0, 99, 2999}; + SMALL_RECT tmp = readRegion; + BOOL ret = ReadConsoleOutput(conout, buffer, {100, 3000}, {0, 0}, &tmp); + ASSERT(ret && !memcmp(&tmp, &readRegion, sizeof(tmp))); + } + } + + ShowWindow(hwnd, SW_SHOW); + + printf("elapsed: %f\n", tm.elapsed()); + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/GetCh.cc b/src/libs/3rdparty/winpty/misc/GetCh.cc new file mode 100644 index 0000000000..cd6ed1943a --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/GetCh.cc @@ -0,0 +1,20 @@ +#include <conio.h> +#include <ctype.h> +#include <stdio.h> + +int main() { + printf("\nPress any keys -- Ctrl-D exits\n\n"); + + while (true) { + const int ch = getch(); + printf("0x%x", ch); + if (isgraph(ch)) { + printf(" '%c'", ch); + } + printf("\n"); + if (ch == 0x4) { // Ctrl-D + break; + } + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/GetConsolePos.cc b/src/libs/3rdparty/winpty/misc/GetConsolePos.cc new file mode 100644 index 0000000000..1f3cc5316f --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/GetConsolePos.cc @@ -0,0 +1,41 @@ +#include <windows.h> + +#include <stdio.h> + +#include "TestUtil.cc" + +int main() { + const HANDLE conout = openConout(); + + CONSOLE_SCREEN_BUFFER_INFO info = {}; + BOOL ret = GetConsoleScreenBufferInfo(conout, &info); + ASSERT(ret && "GetConsoleScreenBufferInfo failed"); + + trace("cursor=%d,%d", info.dwCursorPosition.X, info.dwCursorPosition.Y); + printf("cursor=%d,%d\n", info.dwCursorPosition.X, info.dwCursorPosition.Y); + + trace("srWindow={L=%d,T=%d,R=%d,B=%d}", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom); + printf("srWindow={L=%d,T=%d,R=%d,B=%d}\n", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom); + + trace("dwSize=%d,%d", info.dwSize.X, info.dwSize.Y); + printf("dwSize=%d,%d\n", info.dwSize.X, info.dwSize.Y); + + const HWND hwnd = GetConsoleWindow(); + if (hwnd != NULL) { + RECT r = {}; + if (GetWindowRect(hwnd, &r)) { + const int w = r.right - r.left; + const int h = r.bottom - r.top; + trace("hwnd: pos=(%d,%d) size=(%d,%d)", r.left, r.top, w, h); + printf("hwnd: pos=(%d,%d) size=(%d,%d)\n", r.left, r.top, w, h); + } else { + trace("GetWindowRect failed"); + printf("GetWindowRect failed\n"); + } + } else { + trace("GetConsoleWindow returned NULL"); + printf("GetConsoleWindow returned NULL\n"); + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/GetFont.cc b/src/libs/3rdparty/winpty/misc/GetFont.cc new file mode 100644 index 0000000000..38625317ab --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/GetFont.cc @@ -0,0 +1,261 @@ +#include <windows.h> +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +#include "../src/shared/OsModule.h" +#include "../src/shared/StringUtil.h" + +#include "TestUtil.cc" +#include "../src/shared/StringUtil.cc" + +#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0])) + +// Some of these types and functions are missing from the MinGW headers. +// Others are undocumented. + +struct AGENT_CONSOLE_FONT_INFO { + DWORD nFont; + COORD dwFontSize; +}; + +struct AGENT_CONSOLE_FONT_INFOEX { + ULONG cbSize; + DWORD nFont; + COORD dwFontSize; + UINT FontFamily; + UINT FontWeight; + WCHAR FaceName[LF_FACESIZE]; +}; + +// undocumented XP API +typedef BOOL WINAPI SetConsoleFont_t( + HANDLE hOutput, + DWORD dwFontIndex); + +// undocumented XP API +typedef DWORD WINAPI GetNumberOfConsoleFonts_t(); + +// XP and up +typedef BOOL WINAPI GetCurrentConsoleFont_t( + HANDLE hOutput, + BOOL bMaximumWindow, + AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont); + +// XP and up +typedef COORD WINAPI GetConsoleFontSize_t( + HANDLE hConsoleOutput, + DWORD nFont); + +// Vista and up +typedef BOOL WINAPI GetCurrentConsoleFontEx_t( + HANDLE hConsoleOutput, + BOOL bMaximumWindow, + AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx); + +// Vista and up +typedef BOOL WINAPI SetCurrentConsoleFontEx_t( + HANDLE hConsoleOutput, + BOOL bMaximumWindow, + AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx); + +#define GET_MODULE_PROC(mod, funcName) \ + m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \ + +#define DEFINE_ACCESSOR(funcName) \ + funcName##_t &funcName() const { \ + ASSERT(valid()); \ + return *m_##funcName; \ + } + +class XPFontAPI { +public: + XPFontAPI() : m_kernel32(L"kernel32.dll") { + GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont); + GET_MODULE_PROC(m_kernel32, GetConsoleFontSize); + } + + bool valid() const { + return m_GetCurrentConsoleFont != NULL && + m_GetConsoleFontSize != NULL; + } + + DEFINE_ACCESSOR(GetCurrentConsoleFont) + DEFINE_ACCESSOR(GetConsoleFontSize) + +private: + OsModule m_kernel32; + GetCurrentConsoleFont_t *m_GetCurrentConsoleFont; + GetConsoleFontSize_t *m_GetConsoleFontSize; +}; + +class UndocumentedXPFontAPI : public XPFontAPI { +public: + UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") { + GET_MODULE_PROC(m_kernel32, SetConsoleFont); + GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts); + } + + bool valid() const { + return this->XPFontAPI::valid() && + m_SetConsoleFont != NULL && + m_GetNumberOfConsoleFonts != NULL; + } + + DEFINE_ACCESSOR(SetConsoleFont) + DEFINE_ACCESSOR(GetNumberOfConsoleFonts) + +private: + OsModule m_kernel32; + SetConsoleFont_t *m_SetConsoleFont; + GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts; +}; + +class VistaFontAPI : public XPFontAPI { +public: + VistaFontAPI() : m_kernel32(L"kernel32.dll") { + GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx); + GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx); + } + + bool valid() const { + return this->XPFontAPI::valid() && + m_GetCurrentConsoleFontEx != NULL && + m_SetCurrentConsoleFontEx != NULL; + } + + DEFINE_ACCESSOR(GetCurrentConsoleFontEx) + DEFINE_ACCESSOR(SetCurrentConsoleFontEx) + +private: + OsModule m_kernel32; + GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx; + SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx; +}; + +static std::vector<std::pair<DWORD, COORD> > readFontTable( + XPFontAPI &api, HANDLE conout, DWORD maxCount) { + std::vector<std::pair<DWORD, COORD> > ret; + for (DWORD i = 0; i < maxCount; ++i) { + COORD size = api.GetConsoleFontSize()(conout, i); + if (size.X == 0 && size.Y == 0) { + break; + } + ret.push_back(std::make_pair(i, size)); + } + return ret; +} + +static void dumpFontTable(HANDLE conout) { + const int kMaxCount = 1000; + XPFontAPI api; + if (!api.valid()) { + printf("dumpFontTable: cannot dump font table -- missing APIs\n"); + return; + } + std::vector<std::pair<DWORD, COORD> > table = + readFontTable(api, conout, kMaxCount); + std::string line; + char tmp[128]; + size_t first = 0; + while (first < table.size()) { + size_t last = std::min(table.size() - 1, first + 10 - 1); + winpty_snprintf(tmp, "%02u-%02u:", + static_cast<unsigned>(first), static_cast<unsigned>(last)); + line = tmp; + for (size_t i = first; i <= last; ++i) { + if (i % 10 == 5) { + line += " - "; + } + winpty_snprintf(tmp, " %2dx%-2d", + table[i].second.X, table[i].second.Y); + line += tmp; + } + printf("%s\n", line.c_str()); + first = last + 1; + } + if (table.size() == kMaxCount) { + printf("... stopped reading at %d fonts ...\n", kMaxCount); + } +} + +static std::string stringToCodePoints(const std::wstring &str) { + std::string ret = "("; + for (size_t i = 0; i < str.size(); ++i) { + char tmp[32]; + winpty_snprintf(tmp, "%X", str[i]); + if (ret.size() > 1) { + ret.push_back(' '); + } + ret += tmp; + } + ret.push_back(')'); + return ret; +} + +static void dumpFontInfoEx( + const AGENT_CONSOLE_FONT_INFOEX &infoex) { + std::wstring faceName(infoex.FaceName, + winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName))); + cprintf(L"nFont=%u dwFontSize=(%d,%d) " + "FontFamily=0x%x FontWeight=%u FaceName=%ls %hs\n", + static_cast<unsigned>(infoex.nFont), + infoex.dwFontSize.X, infoex.dwFontSize.Y, + infoex.FontFamily, infoex.FontWeight, faceName.c_str(), + stringToCodePoints(faceName).c_str()); +} + +static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, BOOL maxWindow) { + AGENT_CONSOLE_FONT_INFOEX infoex = {0}; + infoex.cbSize = sizeof(infoex); + if (!api.GetCurrentConsoleFontEx()(conout, maxWindow, &infoex)) { + printf("GetCurrentConsoleFontEx call failed\n"); + return; + } + dumpFontInfoEx(infoex); +} + +static void dumpXPFont(XPFontAPI &api, HANDLE conout, BOOL maxWindow) { + AGENT_CONSOLE_FONT_INFO info = {0}; + if (!api.GetCurrentConsoleFont()(conout, maxWindow, &info)) { + printf("GetCurrentConsoleFont call failed\n"); + return; + } + printf("nFont=%u dwFontSize=(%d,%d)\n", + static_cast<unsigned>(info.nFont), + info.dwFontSize.X, info.dwFontSize.Y); +} + +static void dumpFontAndTable(HANDLE conout) { + VistaFontAPI vista; + if (vista.valid()) { + printf("maxWnd=0: "); dumpVistaFont(vista, conout, FALSE); + printf("maxWnd=1: "); dumpVistaFont(vista, conout, TRUE); + dumpFontTable(conout); + return; + } + UndocumentedXPFontAPI xp; + if (xp.valid()) { + printf("maxWnd=0: "); dumpXPFont(xp, conout, FALSE); + printf("maxWnd=1: "); dumpXPFont(xp, conout, TRUE); + dumpFontTable(conout); + return; + } + printf("setSmallFont: neither Vista nor XP APIs detected -- giving up\n"); + dumpFontTable(conout); +} + +int main() { + const HANDLE conout = openConout(); + const COORD largest = GetLargestConsoleWindowSize(conout); + printf("largestConsoleWindowSize=(%d,%d)\n", largest.X, largest.Y); + dumpFontAndTable(conout); + UndocumentedXPFontAPI xp; + if (xp.valid()) { + printf("GetNumberOfConsoleFonts returned %u\n", xp.GetNumberOfConsoleFonts()()); + } else { + printf("The GetNumberOfConsoleFonts API was missing\n"); + } + printf("CP=%u OutputCP=%u\n", GetConsoleCP(), GetConsoleOutputCP()); + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1 b/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1 new file mode 100644 index 0000000000..0c488597bd --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1 @@ -0,0 +1,51 @@ +# +# Usage: powershell <path>\IdentifyConsoleWindow.ps1 +# +# This script determines whether the process has a console attached, whether +# that console has a non-NULL window (e.g. HWND), and whether the window is on +# the current window station. +# + +$signature = @' +[DllImport("kernel32.dll", SetLastError=true)] +public static extern IntPtr GetConsoleWindow(); + +[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] +public static extern bool SetConsoleTitle(String title); + +[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)] +public static extern int GetWindowText(IntPtr hWnd, + System.Text.StringBuilder lpString, + int nMaxCount); +'@ + +$WinAPI = Add-Type -MemberDefinition $signature ` + -Name WinAPI -Namespace IdentifyConsoleWindow -PassThru + +if (!$WinAPI::SetConsoleTitle("ConsoleWindowScript")) { + echo "error: could not change console title -- is a console attached?" + exit 1 +} else { + echo "note: successfully set console title to ""ConsoleWindowScript""." +} + +$hwnd = $WinAPI::GetConsoleWindow() +if ($hwnd -eq 0) { + echo "note: GetConsoleWindow returned NULL." +} else { + echo "note: GetConsoleWindow returned 0x$($hwnd.ToString("X"))." + $sb = New-Object System.Text.StringBuilder -ArgumentList 4096 + if ($WinAPI::GetWindowText($hwnd, $sb, $sb.Capacity)) { + $title = $sb.ToString() + echo "note: GetWindowText returned ""${title}""." + if ($title -eq "ConsoleWindowScript") { + echo "success!" + } else { + echo "error: expected to see ""ConsoleWindowScript""." + echo " (Perhaps the console window is on a different window station?)" + } + } else { + echo "error: GetWindowText could not read the window title." + echo " (Perhaps the console window is on a different window station?)" + } +} diff --git a/src/libs/3rdparty/winpty/misc/IsNewConsole.cc b/src/libs/3rdparty/winpty/misc/IsNewConsole.cc new file mode 100644 index 0000000000..2b554c72c9 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/IsNewConsole.cc @@ -0,0 +1,87 @@ +// Determines whether this is a new console by testing whether MARK moves the +// cursor. +// +// WARNING: This test program may behave erratically if run under winpty. +// + +#include <windows.h> + +#include <stdio.h> +#include <string.h> + +#include "TestUtil.cc" + +const int SC_CONSOLE_MARK = 0xFFF2; +const int SC_CONSOLE_SELECT_ALL = 0xFFF5; + +static COORD getWindowPos(HANDLE conout) { + CONSOLE_SCREEN_BUFFER_INFO info = {}; + BOOL ret = GetConsoleScreenBufferInfo(conout, &info); + ASSERT(ret && "GetConsoleScreenBufferInfo failed"); + return { info.srWindow.Left, info.srWindow.Top }; +} + +static COORD getWindowSize(HANDLE conout) { + CONSOLE_SCREEN_BUFFER_INFO info = {}; + BOOL ret = GetConsoleScreenBufferInfo(conout, &info); + ASSERT(ret && "GetConsoleScreenBufferInfo failed"); + return { + static_cast<short>(info.srWindow.Right - info.srWindow.Left + 1), + static_cast<short>(info.srWindow.Bottom - info.srWindow.Top + 1) + }; +} + +static COORD getCursorPos(HANDLE conout) { + CONSOLE_SCREEN_BUFFER_INFO info = {}; + BOOL ret = GetConsoleScreenBufferInfo(conout, &info); + ASSERT(ret && "GetConsoleScreenBufferInfo failed"); + return info.dwCursorPosition; +} + +static void setCursorPos(HANDLE conout, COORD pos) { + BOOL ret = SetConsoleCursorPosition(conout, pos); + ASSERT(ret && "SetConsoleCursorPosition failed"); +} + +int main() { + const HANDLE conout = openConout(); + const HWND hwnd = GetConsoleWindow(); + ASSERT(hwnd != NULL && "GetConsoleWindow() returned NULL"); + + // With the legacy console, the Mark command moves the the cursor to the + // top-left cell of the visible console window. Determine whether this + // is the new console by seeing if the cursor moves. + + const auto windowSize = getWindowSize(conout); + if (windowSize.X <= 1) { + printf("Error: console window must be at least 2 columns wide\n"); + trace("Error: console window must be at least 2 columns wide"); + return 1; + } + + bool cursorMoved = false; + const auto initialPos = getCursorPos(conout); + + const auto windowPos = getWindowPos(conout); + setCursorPos(conout, { static_cast<short>(windowPos.X + 1), windowPos.Y }); + + { + const auto posA = getCursorPos(conout); + SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0); + const auto posB = getCursorPos(conout); + cursorMoved = memcmp(&posA, &posB, sizeof(posA)) != 0; + SendMessage(hwnd, WM_CHAR, 27, 0x00010001); // Send ESCAPE + } + + setCursorPos(conout, initialPos); + + if (cursorMoved) { + printf("Legacy console (i.e. MARK moved cursor)\n"); + trace("Legacy console (i.e. MARK moved cursor)"); + } else { + printf("Windows 10 new console (i.e MARK did not move cursor)\n"); + trace("Windows 10 new console (i.e MARK did not move cursor)"); + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt b/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt new file mode 100644 index 0000000000..18460c6861 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt @@ -0,0 +1,90 @@ +Introduction +============ + +The only specification I could find describing mouse input escape sequences +was the /usr/share/doc/xterm/ctlseqs.txt.gz file installed on my Ubuntu +machine. + +Here are the relevant escape sequences: + + * [ON] CSI '?' M 'h' Enable mouse input mode M + * [OFF] CSI '?' M 'l' Disable mouse input mode M + * [EVT] CSI 'M' F X Y Mouse event (default or mode 1005) + * [EVT6] CSI '<' F ';' X ';' Y 'M' Mouse event with mode 1006 + * [EVT6] CSI '<' F ';' X ';' Y 'm' Mouse event with mode 1006 (up) + * [EVT15] CSI F ';' X ';' Y 'M' Mouse event with mode 1015 + +The first batch of modes affect what events are reported: + + * 9: Presses only (not as well-supported as the other modes) + * 1000: Presses and releases + * 1002: Presses, releases, and moves-while-pressed + * 1003: Presses, releases, and all moves + +The next batch of modes affect the encoding of the mouse events: + + * 1005: The X and Y coordinates are UTF-8 codepoints rather than bytes. + * 1006: Use the EVT6 sequences instead of EVT + * 1015: Use the EVT15 sequence instead of EVT (aka URVXT-mode) + +Support for modes in existing terminals +======================================= + + | 9 1000 1002 1003 | 1004 | overflow | defhi | 1005 1006 1015 +---------------------------------+---------------------+------+--------------+-------+---------------- +Eclipse TM Terminal (Neon) | _ _ _ _ | _ | n/a | n/a | _ _ _ +gnome-terminal 3.6.2 | X X X X | _ | suppressed*b | 0x07 | _ X X +iTerm2 2.1.4 | _ X X X | OI | wrap*z | n/a | X X X +jediterm/IntelliJ | _ X X X | _ | ch='?' | 0xff | X X X +Konsole 2.13.2 | _ X X *a | _ | suppressed | 0xff | X X X +mintty 2.2.2 | X X X X | OI | ch='\0' | 0xff | X X X +putty 0.66 | _ X X _ | _ | suppressed | 0xff | _ X X +rxvt 2.7.10 | X X _ _ | _ | wrap*z | n/a | _ _ _ +screen(under xterm) | X X X X | _ | suppressed | 0xff | _ _ _ +urxvt 9.21 | X X X X | _ | wrap*z | n/a | X _ X +xfce4-terminal 0.6.3 (GTK2 VTE) | X X X X | _ | wrap | n/a | _ _ _ +xterm | X X X X | OI | ch='\0' | 0xff | X X X + +*a: Mode 1003 is handled the same way as 1002. +*b: The coordinate wraps from 0xff to 0x00, then maxs out at 0x07. I'm + guessing this behavior is a bug? I'm using the Xubuntu 14.04 + gnome-terminal. +*z: These terminals have a bug where column 224 (and row 224, presumably) + yields a truncated escape sequence. 224 + 32 is 0, so it would normally + yield `CSI 'M' F '\0' Y`, but the '\0' is interpreted as a NUL-terminator. + +Problem 1: How do these flags work? +=================================== + +Terminals accept the OFF sequence with any of the input modes. This makes +little sense--there are two multi-value settings, not seven independent flags! + +All the terminals handle Granularity the same way. ON-Granularity sets +Granularity to the specified value, and OFF-Granularity sets Granularity to +OFF. + +Terminals vary in how they handle the Encoding modes. For example: + + * xterm. ON-Encoding sets Encoding. OFF-Encoding with a non-active Encoding + has no effect. OFF-Encoding otherwise resets Encoding to Default. + + * mintty (tested 2.2.2), iTerm2 2.1.4, and jediterm. ON-Encoding sets + Encoding. OFF-Encoding resets Encoding to Default. + + * Konsole (tested 2.13.2) seems to configure each encoding method + independently. The effective Encoding is the first enabled encoding in this + list: + - Mode 1006 + - Mode 1015 + - Mode 1005 + - Default + + * gnome-terminal (tested 3.6.2) also configures each encoding method + independently. The effective Encoding is the first enabled encoding in + this list: + - Mode 1006 + - Mode 1015 + - Default + Mode 1005 is not supported. + + * xfce4 terminal 0.6.3 (GTK2 VTE) always outputs the default encoding method. diff --git a/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc b/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc new file mode 100644 index 0000000000..7d9684fe94 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc @@ -0,0 +1,34 @@ +#include <windows.h> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + if (argc != 3 && argc != 5) { + printf("Usage: %s x y\n", argv[0]); + printf("Usage: %s x y width height\n", argv[0]); + return 1; + } + + HWND hwnd = GetConsoleWindow(); + + const int x = atoi(argv[1]); + const int y = atoi(argv[2]); + + int w = 0, h = 0; + if (argc == 3) { + RECT r = {}; + BOOL ret = GetWindowRect(hwnd, &r); + ASSERT(ret && "GetWindowRect failed on console window"); + w = r.right - r.left; + h = r.bottom - r.top; + } else { + w = atoi(argv[3]); + h = atoi(argv[4]); + } + + BOOL ret = MoveWindow(hwnd, x, y, w, h, TRUE); + trace("MoveWindow: ret=%d", ret); + printf("MoveWindow: ret=%d\n", ret); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Notes.txt b/src/libs/3rdparty/winpty/misc/Notes.txt new file mode 100644 index 0000000000..410e184198 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Notes.txt @@ -0,0 +1,219 @@ +Test programs +------------- + +Cygwin + emacs + vim + mc (Midnight Commander) + lynx + links + less + more + wget + +Capturing the console output +---------------------------- + +Initial idea: + +In the agent, keep track of the remote terminal state for N lines of +(window+history). Also keep track of the terminal size. Regularly poll for +changes to the console screen buffer, then use some number of edits to bring +the remote terminal into sync with the console. + +This idea seems to have trouble when a Unix terminal is resized. When the +server receives a resize notification, it can have a hard time figuring out +what the terminal did. Race conditions might also be a problem. + +The behavior of the terminal can be tricky: + + - When the window is expanded by one line, does the terminal add a blank line + to the bottom or move a line from the history into the top? + + - When the window is shrunk by one line, does the terminal delete the topmost + or the bottommost line? Can it delete the line with the cursor? + +Some popular behaviors for expanding: + - [all] If there are no history lines, then add a line at the bottom. + - [konsole] Always add a line at the bottom. + - [putty,xterm,rxvt] Pull in a history line from the top. + - [g-t] I can't tell. It seems to add a blank line, until the program writes + to stdout or until I click the scroll bar, then the output "snaps" back down, + pulling lines out of the history. I thought I saw different behavior + between Ubuntu 10.10 and 11.10, so maybe GNOME 3 changed something. Avoid + using "bash" to test this behavior because "bash" apparently always writes + the prompt after terminal resize. + +Some popular behaviors for shrinking: + - [konsole,putty,xterm,rxvt] If the line at the bottom is blank, then delete + it. Otherwise, move the topmost line into history. + - [g-t] If the line at the bottom has not been touched, then delete it. + Otherwise, move the topmost line into history. + +(TODO: I need to test my theories about the terminal behavior better still. +It's interesting to see how g-t handles clear differently than every other +terminal.) + +There is an ANSI escape sequence (DSR) that sends the current cursor location +to the terminal's input. One idea I had was to use this code to figure out how +the terminal had handled a resize. I currently think this idea won't work due +to race conditions. + +Newer idea: + +Keep track of the last N lines that have been sent to the remote terminal. +Poll for changes to console output. When the output changes, send just the +changed content to the terminal. In particular: + - Don't send a cursor position (CUP) code. Instead, if the line that's 3 + steps up from the latest line changes, send a relative cursor up (CUU) + code. It's OK to send an absolute column number code (CHA). + - At least in general, don't try to send complete screenshots of the current + console window. + +The idea is that sending just the changes should have good behavior for streams +of output, even when those streams modify the output (e.g. an archiver, or +maybe a downloader/packager/wget). I need to think about whether this works +for full-screen programs (e.g. emacs, less, lynx, the above list of programs). + +I noticed that console programs don't typically modify the window or buffer +coordinates. edit.com is an exception. + +I tested the pager in native Python (more?), and I verified that ENTER and SPACE +both paid no attention to the location of the console window within the screen +buffer. This makes sense -- why would they care? The Cygwin less, on the other +hand, does care. If I scroll the window up, then Cygwin less will write to a +position within the window. I didn't really expect this behavior, but it +doesn't seem to be a problem. + +Setting up a TestNetServer service +---------------------------------- + +First run the deploy.sh script to copy files into deploy. Make sure +TestNetServer.exe will run in a bare environment (no MinGW or Qt in the path). + +Install the Windows Server 2003 Resource Kit. It will have two programs in it, +instsrv and srvany. + +Run: + + InstSrv TestNetServer <path-to-srvany>\srvany.exe + +This creates a service named "TestNetServer" that uses the Microsoft service +wrapper. To configure the new service to run TestNetServer, set a registry +value: + + [HKLM\SYSTEM\CurrentControlSet\Services\TestNetServer\Parameters] + Application=<full-path>\TestNetServer.exe + +Also see http://www.iopus.com/guides/srvany.htm. + +To remove the service, run: + + InstSrv TestNetServer REMOVE + +TODO +---- + +Agent: When resizing the console, consider whether to add lines to the top +or bottom. I remember thinking the current behavior was wrong for some +application, but I forgot which one. + +Make the font as small as possible. The console window dimensions are limited by +the screen size, so making the font small reduces an unnecessary limitation on the +PseudoConsole size. There's a documented Vista/Win7 API for this +(SetCurrentConsoleFontEx), and apparently WinXP has an undocumented API +(SetConsoleFont): + http://blogs.microsoft.co.il/blogs/pavely/archive/2009/07/23/changing-console-fonts.aspx + +Make the agent work with DOS programs like edit and qbasic. + - Detect that the terminal program has resized the window/buffer and enter a + simple just-scrape-and-dont-resize mode. Track the client window size and + send the intersection of the console and the agent's client. + - I also need to generate keyboard scan codes. + - Solve the NTVDM.EXE console shutdown problem, probably by ignoring NTVDM.EXE + when it appears on the GetConsoleProcessList list. + +Rename the agent? Is the term "proxy" more accurate? + +Optimize the polling. e.g. Use a longer poll interval when the console is idle. +Do a minimal poll that checks whether the sync marker or window has moved. + +Increase the console buffer size to ~9000 lines. Beware making it so big that +reading the sync column exhausts the 32KB conhost<->agent heap. + +Reduce the memory overhead of the agent. The agent's m_bufferData array can +be small (a few hundred lines?) relative to the console buffer size. + +Try to handle console background color better. + Unix terminal emulators have a user-configurable foreground and background +color, and for best results, the agent really needs to avoid changing the colors, +especially the background color. It's undesirable/ugly to SSH into a machine +and see the command prompt change the colors. It's especially ugly that the +terminal retains its original colors and only drawn cells get the new colors. +(e.g. Resizing the window to the right uses the local terminal colors rather +than the remote colors.) It's especially ugly in gnome-terminal, which draws +user-configurable black as black, but VT100 black as dark-gray. + If there were a way to query the terminal emulator's colors, then I could +match the console's colors to the terminal and everything would just work. As +far as I know, that's not possible. + I thought of a kludge that might work. Instead of translating console white +and black to VT/100 white and black, I would translate them to "reset" and +"invert". I'd translate other colors normally. This approach should produce +ideal results for command-line work and tolerable results for full-screen +programs without configuration. Configuring the agent for black-on-white or +white-on-black would produce ideal results in all situations. + This kludge only really applies to the SSH application. For a Win32 Konsole +application, it should be easy to get the colors right all the time. + +Try using the screen reader API: + - To eliminate polling. + - To detect when a line wraps. When a line wraps, it'd be nice not to send a + CRLF to the terminal emulator so copy-and-paste works better. + - To detect hard tabs with Cygwin. + +Implement VT100/ANSI escape sequence recognition for input. Decide where this +functionality belongs. PseudoConsole.dll? Disambiguating ESC from an escape +sequence might be tricky. For the SSH server, I was thinking that when a small +SSH payload ended with an ESC character, I could assume the character was really +an ESC keypress, on the assumption that if it were an escape sequence, the +payload would probably contain the whole sequence. I'm not sure this works, +especially if there's a lot of other traffic multiplexed on the SSH socket. + +Support Unicode. + - Some DOS programs draw using line/box characters. Can these characters be + translated to the Unicode equivalents? + +Create automated tests. + +Experiment with the Terminator emulator, an emulator that doesn't wrap lines. +How many columns does it report having? What column does it report the cursor +in as it's writing past the right end of the window? Will Terminator be a +problem if I implement line wrapping detection in the agent? + +BUG: After the unix-adapter/pconsole.exe program exits, the blinking cursor is +replaced with a hidden cursor. + +Fix assert() in the agent. If it fails, the failure message needs to be +reported somewhere. Pop up a dialog box? Maybe switch the active desktop, +then show a dialog box? + +TODO: There's already a pconsole project on GitHub. Maybe rename this project +to something else? winpty? + +TODO: Can the DebugServer system be replaced with OutputDebugString? How +do we decide whose processes' output to collect? + +TODO: Three executables: + build/winpty-agent.exe + build/winpty.dll + build/console.exe + +BUG: Run the pconsole.exe inside another console. As I type dir, I see this: + D:\rprichard\pconsole> + D:\rprichard\pconsole>d + D:\rprichard\pconsole>di + D:\rprichard\pconsole>dir + In the output of "dir", every other line is blank. + There was a bug in Terminal::sendLine that was causing this to happen + frequently. Now that I fixed it, this bug should only manifest on lines + whose last column is not a space (i.e. a full line). diff --git a/src/libs/3rdparty/winpty/misc/OSVersion.cc b/src/libs/3rdparty/winpty/misc/OSVersion.cc new file mode 100644 index 0000000000..456708f05b --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/OSVersion.cc @@ -0,0 +1,27 @@ +#include <windows.h> + +#include <assert.h> +#include <locale.h> +#include <stdio.h> + +#include <iostream> + +int main() { + setlocale(LC_ALL, ""); + + OSVERSIONINFOEXW info = {0}; + info.dwOSVersionInfoSize = sizeof(info); + assert(GetVersionExW((OSVERSIONINFOW*)&info)); + + printf("dwMajorVersion = %d\n", (int)info.dwMajorVersion); + printf("dwMinorVersion = %d\n", (int)info.dwMinorVersion); + printf("dwBuildNumber = %d\n", (int)info.dwBuildNumber); + printf("dwPlatformId = %d\n", (int)info.dwPlatformId); + printf("szCSDVersion = %ls\n", info.szCSDVersion); + printf("wServicePackMajor = %d\n", info.wServicePackMajor); + printf("wServicePackMinor = %d\n", info.wServicePackMinor); + printf("wSuiteMask = 0x%x\n", (unsigned int)info.wSuiteMask); + printf("wProductType = 0x%x\n", (unsigned int)info.wProductType); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc new file mode 100644 index 0000000000..656d4f126d --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc @@ -0,0 +1,101 @@ +// +// Verify that console selection blocks writes to an inactive console screen +// buffer. Writes TEST PASSED or TEST FAILED to the popup console window. +// + +#include <windows.h> +#include <stdio.h> + +#include <string> + +#include "TestUtil.cc" + +const int SC_CONSOLE_MARK = 0xFFF2; +const int SC_CONSOLE_SELECT_ALL = 0xFFF5; + +bool g_useMark = false; + +CALLBACK DWORD pausingThread(LPVOID dummy) +{ + HWND hwnd = GetConsoleWindow(); + trace("Sending selection to freeze"); + SendMessage(hwnd, WM_SYSCOMMAND, + g_useMark ? SC_CONSOLE_MARK : + SC_CONSOLE_SELECT_ALL, + 0); + Sleep(1000); + trace("Sending escape WM_CHAR to unfreeze"); + SendMessage(hwnd, WM_CHAR, 27, 0x00010001); + Sleep(1000); +} + +static HANDLE createBuffer() { + HANDLE buf = CreateConsoleScreenBuffer( + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CONSOLE_TEXTMODE_BUFFER, + NULL); + ASSERT(buf != INVALID_HANDLE_VALUE); + return buf; +} + +static void runTest(bool useMark, bool createEarly) { + trace("======================================="); + trace("useMark=%d createEarly=%d", useMark, createEarly); + g_useMark = useMark; + HANDLE buf = INVALID_HANDLE_VALUE; + + if (createEarly) { + buf = createBuffer(); + } + + CreateThread(NULL, 0, + pausingThread, NULL, + 0, NULL); + Sleep(500); + + if (!createEarly) { + trace("Creating buffer"); + TimeMeasurement tm1; + buf = createBuffer(); + const double elapsed1 = tm1.elapsed(); + if (elapsed1 >= 0.250) { + printf("!!! TEST FAILED !!!\n"); + Sleep(2000); + return; + } + } + + trace("Writing to aux buffer"); + TimeMeasurement tm2; + DWORD actual = 0; + BOOL ret = WriteConsoleW(buf, L"HI", 2, &actual, NULL); + const double elapsed2 = tm2.elapsed(); + trace("Writing to aux buffer: finished: ret=%d actual=%d (elapsed=%1.3f)", ret, actual, elapsed2); + if (elapsed2 < 0.250) { + printf("!!! TEST FAILED !!!\n"); + } else { + printf("TEST PASSED\n"); + } + Sleep(2000); +} + +int main(int argc, char **argv) { + if (argc == 1) { + startChildProcess(L"child"); + return 0; + } + + std::string arg = argv[1]; + if (arg == "child") { + for (int useMark = 0; useMark <= 1; useMark++) { + for (int createEarly = 0; createEarly <= 1; createEarly++) { + runTest(useMark, createEarly); + } + } + printf("done...\n"); + Sleep(1000); + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc new file mode 100644 index 0000000000..fa584b9fae --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc @@ -0,0 +1,671 @@ +// +// Windows versions tested +// +// Vista Enterprise SP2 32-bit +// - ver reports [Version 6.0.6002] +// - kernel32.dll product/file versions are 6.0.6002.19381 +// +// Windows 7 Ultimate SP1 32-bit +// - ver reports [Version 6.1.7601] +// - conhost.exe product/file versions are 6.1.7601.18847 +// - kernel32.dll product/file versions are 6.1.7601.18847 +// +// Windows Server 2008 R2 Datacenter SP1 64-bit +// - ver reports [Version 6.1.7601] +// - conhost.exe product/file versions are 6.1.7601.23153 +// - kernel32.dll product/file versions are 6.1.7601.23153 +// +// Windows 8 Enterprise 32-bit +// - ver reports [Version 6.2.9200] +// - conhost.exe product/file versions are 6.2.9200.16578 +// - kernel32.dll product/file versions are 6.2.9200.16859 +// + +// +// Specific version details on working Server 2008 R2: +// +// dwMajorVersion = 6 +// dwMinorVersion = 1 +// dwBuildNumber = 7601 +// dwPlatformId = 2 +// szCSDVersion = Service Pack 1 +// wServicePackMajor = 1 +// wServicePackMinor = 0 +// wSuiteMask = 0x190 +// wProductType = 0x3 +// +// Specific version details on broken Win7: +// +// dwMajorVersion = 6 +// dwMinorVersion = 1 +// dwBuildNumber = 7601 +// dwPlatformId = 2 +// szCSDVersion = Service Pack 1 +// wServicePackMajor = 1 +// wServicePackMinor = 0 +// wSuiteMask = 0x100 +// wProductType = 0x1 +// + +#include <windows.h> +#include <stdio.h> +#include <string.h> + +#include "TestUtil.cc" + +const char *g_prefix = ""; + +static void dumpHandles() { + trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x", + g_prefix, + (long long)GetStdHandle(STD_INPUT_HANDLE), + (long long)GetStdHandle(STD_OUTPUT_HANDLE), + (long long)GetStdHandle(STD_ERROR_HANDLE)); +} + +static const char *successOrFail(BOOL ret) { + return ret ? "ok" : "FAILED"; +} + +static void startChildInSameConsole(const wchar_t *args, BOOL + bInheritHandles=FALSE) { + wchar_t program[1024]; + wchar_t cmdline[1024]; + GetModuleFileNameW(NULL, program, 1024); + swprintf(cmdline, L"\"%ls\" %ls", program, args); + + STARTUPINFOW sui; + PROCESS_INFORMATION pi; + memset(&sui, 0, sizeof(sui)); + memset(&pi, 0, sizeof(pi)); + sui.cb = sizeof(sui); + + CreateProcessW(program, cmdline, + NULL, NULL, + /*bInheritHandles=*/bInheritHandles, + /*dwCreationFlags=*/0, + NULL, NULL, + &sui, &pi); +} + +static void closeHandle(HANDLE h) { + trace("%sClosing handle 0x%I64x...", g_prefix, (long long)h); + trace("%sClosing handle 0x%I64x... %s", g_prefix, (long long)h, successOrFail(CloseHandle(h))); +} + +static HANDLE createBuffer() { + + // If sa isn't provided, the handle defaults to not-inheritable. + SECURITY_ATTRIBUTES sa = {0}; + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + + trace("%sCreating a new buffer...", g_prefix); + HANDLE conout = CreateConsoleScreenBuffer( + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + CONSOLE_TEXTMODE_BUFFER, NULL); + + trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout); + return conout; +} + +static HANDLE openConout() { + + // If sa isn't provided, the handle defaults to not-inheritable. + SECURITY_ATTRIBUTES sa = {0}; + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + + trace("%sOpening CONOUT...", g_prefix); + HANDLE conout = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + OPEN_EXISTING, 0, NULL); + trace("%sOpening CONOUT... 0x%I64x", g_prefix, (long long)conout); + return conout; +} + +static void setConsoleActiveScreenBuffer(HANDLE conout) { + trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...", + g_prefix, (long long)conout); + trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s", + g_prefix, (long long)conout, + successOrFail(SetConsoleActiveScreenBuffer(conout))); +} + +static void writeTest(HANDLE conout, const char *msg) { + char writeData[256]; + sprintf(writeData, "%s%s\n", g_prefix, msg); + + trace("%sWriting to 0x%I64x: '%s'...", + g_prefix, (long long)conout, msg); + DWORD actual = 0; + BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL); + trace("%sWriting to 0x%I64x: '%s'... %s", + g_prefix, (long long)conout, msg, + successOrFail(ret && actual == strlen(writeData))); +} + +static void writeTest(const char *msg) { + writeTest(GetStdHandle(STD_OUTPUT_HANDLE), msg); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// TEST 1 -- create new buffer, activate it, and close the handle. The console +// automatically switches the screen buffer back to the original. +// +// This test passes everywhere. +// + +static void test1(int argc, char *argv[]) { + if (!strcmp(argv[1], "1")) { + startChildProcess(L"1:child"); + return; + } + + HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); + writeTest(origBuffer, "<-- origBuffer -->"); + + HANDLE newBuffer = createBuffer(); + writeTest(newBuffer, "<-- newBuffer -->"); + setConsoleActiveScreenBuffer(newBuffer); + Sleep(2000); + + writeTest(origBuffer, "TEST PASSED!"); + + // Closing the handle w/o switching the active screen buffer automatically + // switches the console back to the original buffer. + closeHandle(newBuffer); + + while (true) { + Sleep(1000); + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// TEST 2 -- Test program that creates and activates newBuffer, starts a child +// process, then closes its newBuffer handle. newBuffer remains activated, +// because the child keeps it active. (Also see TEST D.) +// + +static void test2(int argc, char *argv[]) { + if (!strcmp(argv[1], "2")) { + startChildProcess(L"2:parent"); + return; + } + + if (!strcmp(argv[1], "2:parent")) { + g_prefix = "parent: "; + dumpHandles(); + HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); + writeTest(origBuffer, "<-- origBuffer -->"); + + HANDLE newBuffer = createBuffer(); + writeTest(newBuffer, "<-- newBuffer -->"); + setConsoleActiveScreenBuffer(newBuffer); + + Sleep(1000); + writeTest(newBuffer, "bInheritHandles=FALSE:"); + startChildInSameConsole(L"2:child", FALSE); + Sleep(1000); + writeTest(newBuffer, "bInheritHandles=TRUE:"); + startChildInSameConsole(L"2:child", TRUE); + + Sleep(1000); + trace("parent:----"); + + // Close the new buffer. The active screen buffer doesn't automatically + // switch back to origBuffer, because the child process has a handle open + // to the original buffer. + closeHandle(newBuffer); + + Sleep(600 * 1000); + return; + } + + if (!strcmp(argv[1], "2:child")) { + g_prefix = "child: "; + dumpHandles(); + // The child's output isn't visible, because it's still writing to + // origBuffer. + trace("child:----"); + writeTest("writing to STDOUT"); + + // Handle inheritability is curious. The console handles this program + // creates are inheritable, but CreateProcess is called with both + // bInheritHandles=TRUE and bInheritHandles=FALSE. + // + // Vista and Windows 7: bInheritHandles has no effect. The child and + // parent processes have the same STDIN/STDOUT/STDERR handles: + // 0x3, 0x7, and 0xB. The parent has a 0xF handle for newBuffer. + // The child can only write to 0x7, 0xB, and 0xF. Only the writes to + // 0xF are visible (i.e. they touch newBuffer). + // + // Windows 8 or Windows 10 (legacy or non-legacy): the lowest 2 bits of + // the HANDLE to WriteConsole seem to be ignored. The new process' + // console handles always refer to the buffer that was active when they + // started, but the values of the handles depend upon bInheritHandles. + // With bInheritHandles=TRUE, the child has the same + // STDIN/STDOUT/STDERR/newBuffer handles as the parent, and the three + // output handles all work, though their output is all visible. With + // bInheritHandles=FALSE, the child has different STDIN/STDOUT/STDERR + // handles, and only the new STDOUT/STDERR handles work. + // + for (unsigned int i = 0x1; i <= 0xB0; ++i) { + char msg[256]; + sprintf(msg, "Write to handle 0x%x", i); + HANDLE h = reinterpret_cast<HANDLE>(i); + writeTest(h, msg); + } + + Sleep(600 * 1000); + return; + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// TEST A -- demonstrate an apparent Windows bug with screen buffers +// +// Steps: +// - The parent starts a child process. +// - The child process creates and activates newBuffer +// - The parent opens CONOUT$ and writes to it. +// - The parent closes CONOUT$. +// - At this point, broken Windows reactivates origBuffer. +// - The child writes to newBuffer again. +// - The child activates origBuffer again, then closes newBuffer. +// +// Test passes if the message "TEST PASSED!" is visible. +// Test commonly fails if conhost.exe crashes. +// +// Results: +// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes +// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS +// - Windows 8 Enterprise 32-bit: PASS +// - Windows 10 64-bit (legacy and non-legacy): PASS +// + +static void testA_parentWork() { + // Open an extra CONOUT$ handle so that the HANDLE values in parent and + // child don't collide. I think it's OK if they collide, but since we're + // trying to track down a Windows bug, it's best to avoid unnecessary + // complication. + HANDLE dummy = openConout(); + + Sleep(3000); + + // Step 2: Open CONOUT$ in the parent. This opens the active buffer, which + // was just created in the child. It's handle 0x13. Write to it. + + HANDLE newBuffer = openConout(); + writeTest(newBuffer, "step2: writing to newBuffer"); + + Sleep(3000); + + // Step 3: Close handle 0x13. With Windows 7, the console switches back to + // origBuffer, and (unless I'm missing something) it shouldn't. + + closeHandle(newBuffer); +} + +static void testA_childWork() { + HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); + + // + // Step 1: Create the new screen buffer in the child process and make it + // active. (Typically, it's handle 0x0F.) + // + + HANDLE newBuffer = createBuffer(); + + setConsoleActiveScreenBuffer(newBuffer); + writeTest(newBuffer, "<-- newBuffer -->"); + + Sleep(9000); + trace("child:----"); + + // Step 4: write to the newBuffer again. + writeTest(newBuffer, "TEST PASSED!"); + + // + // Step 5: Switch back to the original screen buffer and close the new + // buffer. The switch call succeeds, but the CloseHandle call freezes for + // several seconds, because conhost.exe crashes. + // + Sleep(3000); + + setConsoleActiveScreenBuffer(origBuffer); + writeTest(origBuffer, "writing to origBuffer"); + + closeHandle(newBuffer); + + // The console HWND is NULL. + trace("child: console HWND=0x%I64x", (long long)GetConsoleWindow()); + + // At this point, the console window has closed, but the parent/child + // processes are still running. Calling AllocConsole would fail, but + // calling FreeConsole followed by AllocConsole would both succeed, and a + // new console would appear. +} + +static void testA(int argc, char *argv[]) { + + if (!strcmp(argv[1], "A")) { + startChildProcess(L"A:parent"); + return; + } + + if (!strcmp(argv[1], "A:parent")) { + g_prefix = "parent: "; + trace("parent:----"); + dumpHandles(); + writeTest("<-- origBuffer -->"); + startChildInSameConsole(L"A:child"); + testA_parentWork(); + Sleep(120000); + return; + } + + if (!strcmp(argv[1], "A:child")) { + g_prefix = "child: "; + dumpHandles(); + testA_childWork(); + Sleep(120000); + return; + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// TEST B -- invert TEST A -- also crashes conhost on Windows 7 +// +// Test passes if the message "TEST PASSED!" is visible. +// Test commonly fails if conhost.exe crashes. +// +// Results: +// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes +// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS +// - Windows 8 Enterprise 32-bit: PASS +// - Windows 10 64-bit (legacy and non-legacy): PASS +// + +static void testB(int argc, char *argv[]) { + if (!strcmp(argv[1], "B")) { + startChildProcess(L"B:parent"); + return; + } + + if (!strcmp(argv[1], "B:parent")) { + g_prefix = "parent: "; + startChildInSameConsole(L"B:child"); + writeTest("<-- origBuffer -->"); + HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); + + // + // Step 1: Create the new buffer and make it active. + // + trace("%s----", g_prefix); + HANDLE newBuffer = createBuffer(); + setConsoleActiveScreenBuffer(newBuffer); + writeTest(newBuffer, "<-- newBuffer -->"); + + // + // Step 4: Attempt to write again to the new buffer. + // + Sleep(9000); + trace("%s----", g_prefix); + writeTest(newBuffer, "TEST PASSED!"); + + // + // Step 5: Switch back to the original buffer. + // + Sleep(3000); + trace("%s----", g_prefix); + setConsoleActiveScreenBuffer(origBuffer); + closeHandle(newBuffer); + writeTest(origBuffer, "writing to the initial buffer"); + + Sleep(60000); + return; + } + + if (!strcmp(argv[1], "B:child")) { + g_prefix = "child: "; + Sleep(3000); + trace("%s----", g_prefix); + + // + // Step 2: Open the newly active buffer and write to it. + // + HANDLE newBuffer = openConout(); + writeTest(newBuffer, "writing to newBuffer"); + + // + // Step 3: Close the newly active buffer. + // + Sleep(3000); + closeHandle(newBuffer); + + Sleep(60000); + return; + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// TEST C -- Interleaving open/close of console handles also seems to break on +// Windows 7. +// +// Test: +// - child creates and activates newBuf1 +// - parent opens newBuf1 +// - child creates and activates newBuf2 +// - parent opens newBuf2, then closes newBuf1 +// - child switches back to newBuf1 +// * At this point, the console starts malfunctioning. +// - parent and child close newBuf2 +// - child closes newBuf1 +// +// Test passes if the message "TEST PASSED!" is visible. +// Test commonly fails if conhost.exe crashes. +// +// Results: +// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes +// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS +// - Windows 8 Enterprise 32-bit: PASS +// - Windows 10 64-bit (legacy and non-legacy): PASS +// + +static void testC(int argc, char *argv[]) { + if (!strcmp(argv[1], "C")) { + startChildProcess(L"C:parent"); + return; + } + + if (!strcmp(argv[1], "C:parent")) { + startChildInSameConsole(L"C:child"); + writeTest("<-- origBuffer -->"); + g_prefix = "parent: "; + + // At time=4, open newBuffer1. + Sleep(4000); + trace("%s---- t=4", g_prefix); + const HANDLE newBuffer1 = openConout(); + + // At time=8, open newBuffer2, and close newBuffer1. + Sleep(4000); + trace("%s---- t=8", g_prefix); + const HANDLE newBuffer2 = openConout(); + closeHandle(newBuffer1); + + // At time=25, cleanup of newBuffer2. + Sleep(17000); + trace("%s---- t=25", g_prefix); + closeHandle(newBuffer2); + + Sleep(240000); + return; + } + + if (!strcmp(argv[1], "C:child")) { + g_prefix = "child: "; + + // At time=2, create newBuffer1 and activate it. + Sleep(2000); + trace("%s---- t=2", g_prefix); + const HANDLE newBuffer1 = createBuffer(); + setConsoleActiveScreenBuffer(newBuffer1); + writeTest(newBuffer1, "<-- newBuffer1 -->"); + + // At time=6, create newBuffer2 and activate it. + Sleep(4000); + trace("%s---- t=6", g_prefix); + const HANDLE newBuffer2 = createBuffer(); + setConsoleActiveScreenBuffer(newBuffer2); + writeTest(newBuffer2, "<-- newBuffer2 -->"); + + // At time=10, attempt to switch back to newBuffer1. The parent process + // has opened and closed its handle to newBuffer1, so does it still exist? + Sleep(4000); + trace("%s---- t=10", g_prefix); + setConsoleActiveScreenBuffer(newBuffer1); + writeTest(newBuffer1, "write to newBuffer1: TEST PASSED!"); + + // At time=25, cleanup of newBuffer2. + Sleep(15000); + trace("%s---- t=25", g_prefix); + closeHandle(newBuffer2); + + // At time=35, cleanup of newBuffer1. The console should switch to the + // initial buffer again. + Sleep(10000); + trace("%s---- t=35", g_prefix); + closeHandle(newBuffer1); + + Sleep(240000); + return; + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// TEST D -- parent creates a new buffer, child launches, writes, +// closes it output handle, then parent writes again. (Also see TEST 2.) +// +// On success, this will appear: +// +// parent: <-- newBuffer --> +// child: writing to newBuffer +// parent: TEST PASSED! +// +// If this appears, it indicates that the child's closing its output handle did +// not destroy newBuffer. +// +// Results: +// - Windows 7 Ultimate SP1 32-bit: PASS +// - Windows 8 Enterprise 32-bit: PASS +// - Windows 10 64-bit (legacy and non-legacy): PASS +// + +static void testD(int argc, char *argv[]) { + if (!strcmp(argv[1], "D")) { + startChildProcess(L"D:parent"); + return; + } + + if (!strcmp(argv[1], "D:parent")) { + g_prefix = "parent: "; + HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE); + writeTest(origBuffer, "<-- origBuffer -->"); + + HANDLE newBuffer = createBuffer(); + writeTest(newBuffer, "<-- newBuffer -->"); + setConsoleActiveScreenBuffer(newBuffer); + + // At t=2, start a child process, explicitly forcing it to use + // newBuffer for its standard handles. These calls are apparently + // redundant on Windows 8 and up. + Sleep(2000); + trace("parent:----"); + trace("parent: starting child process"); + SetStdHandle(STD_OUTPUT_HANDLE, newBuffer); + SetStdHandle(STD_ERROR_HANDLE, newBuffer); + startChildInSameConsole(L"D:child"); + SetStdHandle(STD_OUTPUT_HANDLE, origBuffer); + SetStdHandle(STD_ERROR_HANDLE, origBuffer); + + // At t=6, write again to newBuffer. + Sleep(4000); + trace("parent:----"); + writeTest(newBuffer, "TEST PASSED!"); + + // At t=8, close the newBuffer. In earlier versions of windows + // (including Server 2008 R2), the console then switches back to + // origBuffer. As of Windows 8, it doesn't, because somehow the child + // process is keeping the console on newBuffer, even though the child + // process closed its STDIN/STDOUT/STDERR handles. Killing the child + // process by hand after the test finishes *does* force the console + // back to origBuffer. + Sleep(2000); + closeHandle(newBuffer); + + Sleep(120000); + return; + } + + if (!strcmp(argv[1], "D:child")) { + g_prefix = "child: "; + // At t=2, the child starts. + trace("child:----"); + dumpHandles(); + writeTest("writing to newBuffer"); + + // At t=4, the child explicitly closes its handle. + Sleep(2000); + trace("child:----"); + if (GetStdHandle(STD_ERROR_HANDLE) != GetStdHandle(STD_OUTPUT_HANDLE)) { + closeHandle(GetStdHandle(STD_ERROR_HANDLE)); + } + closeHandle(GetStdHandle(STD_OUTPUT_HANDLE)); + closeHandle(GetStdHandle(STD_INPUT_HANDLE)); + + Sleep(120000); + return; + } +} + + + +int main(int argc, char *argv[]) { + if (argc == 1) { + printf("USAGE: %s testnum\n", argv[0]); + return 0; + } + + if (argv[1][0] == '1') { + test1(argc, argv); + } else if (argv[1][0] == '2') { + test2(argc, argv); + } else if (argv[1][0] == 'A') { + testA(argc, argv); + } else if (argv[1][0] == 'B') { + testB(argc, argv); + } else if (argv[1][0] == 'C') { + testC(argc, argv); + } else if (argv[1][0] == 'D') { + testD(argc, argv); + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc new file mode 100644 index 0000000000..2b648c9409 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc @@ -0,0 +1,151 @@ +#include <windows.h> + +#include "TestUtil.cc" + +const char *g_prefix = ""; + +static void dumpHandles() { + trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x", + g_prefix, + (long long)GetStdHandle(STD_INPUT_HANDLE), + (long long)GetStdHandle(STD_OUTPUT_HANDLE), + (long long)GetStdHandle(STD_ERROR_HANDLE)); +} + +static HANDLE createBuffer() { + + // If sa isn't provided, the handle defaults to not-inheritable. + SECURITY_ATTRIBUTES sa = {0}; + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + + trace("%sCreating a new buffer...", g_prefix); + HANDLE conout = CreateConsoleScreenBuffer( + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + CONSOLE_TEXTMODE_BUFFER, NULL); + + trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout); + return conout; +} + +static const char *successOrFail(BOOL ret) { + return ret ? "ok" : "FAILED"; +} + +static void setConsoleActiveScreenBuffer(HANDLE conout) { + trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...", + g_prefix, (long long)conout); + trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s", + g_prefix, (long long)conout, + successOrFail(SetConsoleActiveScreenBuffer(conout))); +} + +static void writeTest(HANDLE conout, const char *msg) { + char writeData[256]; + sprintf(writeData, "%s%s\n", g_prefix, msg); + + trace("%sWriting to 0x%I64x: '%s'...", + g_prefix, (long long)conout, msg); + DWORD actual = 0; + BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL); + trace("%sWriting to 0x%I64x: '%s'... %s", + g_prefix, (long long)conout, msg, + successOrFail(ret && actual == strlen(writeData))); +} + +static HANDLE startChildInSameConsole(const wchar_t *args, BOOL + bInheritHandles=FALSE) { + wchar_t program[1024]; + wchar_t cmdline[1024]; + GetModuleFileNameW(NULL, program, 1024); + swprintf(cmdline, L"\"%ls\" %ls", program, args); + + STARTUPINFOW sui; + PROCESS_INFORMATION pi; + memset(&sui, 0, sizeof(sui)); + memset(&pi, 0, sizeof(pi)); + sui.cb = sizeof(sui); + + CreateProcessW(program, cmdline, + NULL, NULL, + /*bInheritHandles=*/bInheritHandles, + /*dwCreationFlags=*/0, + NULL, NULL, + &sui, &pi); + + return pi.hProcess; +} + +static HANDLE dup(HANDLE h, HANDLE targetProcess) { + HANDLE h2 = INVALID_HANDLE_VALUE; + BOOL ret = DuplicateHandle( + GetCurrentProcess(), h, + targetProcess, &h2, + 0, TRUE, DUPLICATE_SAME_ACCESS); + trace("dup(0x%I64x) to process 0x%I64x... %s, 0x%I64x", + (long long)h, + (long long)targetProcess, + successOrFail(ret), + (long long)h2); + return h2; +} + +int main(int argc, char *argv[]) { + if (argc == 1) { + startChildProcess(L"parent"); + return 0; + } + + if (!strcmp(argv[1], "parent")) { + g_prefix = "parent: "; + dumpHandles(); + HANDLE hChild = startChildInSameConsole(L"child"); + + // Windows 10. + HANDLE orig1 = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE new1 = createBuffer(); + + Sleep(2000); + setConsoleActiveScreenBuffer(new1); + + // Handle duplication results to child process in same console: + // - Windows XP: fails + // - Windows 7 Ultimate SP1 32-bit: fails + // - Windows Server 2008 R2 Datacenter SP1 64-bit: fails + // - Windows 8 Enterprise 32-bit: succeeds + // - Windows 10: succeeds + HANDLE orig2 = dup(orig1, GetCurrentProcess()); + HANDLE new2 = dup(new1, GetCurrentProcess()); + + dup(orig1, hChild); + dup(new1, hChild); + + // The writes to orig1/orig2 are invisible. The writes to new1/new2 + // are visible. + writeTest(orig1, "write to orig1"); + writeTest(orig2, "write to orig2"); + writeTest(new1, "write to new1"); + writeTest(new2, "write to new2"); + + Sleep(120000); + return 0; + } + + if (!strcmp(argv[1], "child")) { + g_prefix = "child: "; + dumpHandles(); + Sleep(4000); + for (unsigned int i = 0x1; i <= 0xB0; ++i) { + char msg[256]; + sprintf(msg, "Write to handle 0x%x", i); + HANDLE h = reinterpret_cast<HANDLE>(i); + writeTest(h, msg); + } + Sleep(120000); + return 0; + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/SelectAllTest.cc b/src/libs/3rdparty/winpty/misc/SelectAllTest.cc new file mode 100644 index 0000000000..a6c27739d8 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/SelectAllTest.cc @@ -0,0 +1,45 @@ +#define _WIN32_WINNT 0x0501 +#include <stdio.h> +#include <windows.h> + +#include "../src/shared/DebugClient.cc" + +const int SC_CONSOLE_MARK = 0xFFF2; +const int SC_CONSOLE_SELECT_ALL = 0xFFF5; + +CALLBACK DWORD pausingThread(LPVOID dummy) +{ + HWND hwnd = GetConsoleWindow(); + while (true) { + SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0); + Sleep(1000); + SendMessage(hwnd, WM_CHAR, 27, 0x00010001); + Sleep(1000); + } +} + +int main() +{ + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO info; + + GetConsoleScreenBufferInfo(out, &info); + COORD initial = info.dwCursorPosition; + + CreateThread(NULL, 0, + pausingThread, NULL, + 0, NULL); + + for (int i = 0; i < 30; ++i) { + Sleep(100); + GetConsoleScreenBufferInfo(out, &info); + if (memcmp(&info.dwCursorPosition, &initial, sizeof(COORD)) != 0) { + trace("cursor moved to [%d,%d]", + info.dwCursorPosition.X, + info.dwCursorPosition.Y); + } else { + trace("cursor in expected position"); + } + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/SetBufInfo.cc b/src/libs/3rdparty/winpty/misc/SetBufInfo.cc new file mode 100644 index 0000000000..f37c31bdf7 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/SetBufInfo.cc @@ -0,0 +1,90 @@ +#include <windows.h> + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "TestUtil.cc" + +static void usage() { + printf("usage: SetBufInfo [-set] [-buf W H] [-win W H] [-pos X Y]\n"); +} + +int main(int argc, char *argv[]) { + const HANDLE conout = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + ASSERT(conout != INVALID_HANDLE_VALUE); + + bool change = false; + BOOL success; + CONSOLE_SCREEN_BUFFER_INFOEX info = {}; + info.cbSize = sizeof(info); + + success = GetConsoleScreenBufferInfoEx(conout, &info); + ASSERT(success && "GetConsoleScreenBufferInfoEx failed"); + + for (int i = 1; i < argc; ) { + std::string arg = argv[i]; + if (arg == "-buf" && (i + 2) < argc) { + info.dwSize.X = atoi(argv[i + 1]); + info.dwSize.Y = atoi(argv[i + 2]); + i += 3; + change = true; + } else if (arg == "-pos" && (i + 2) < argc) { + int dx = info.srWindow.Right - info.srWindow.Left; + int dy = info.srWindow.Bottom - info.srWindow.Top; + info.srWindow.Left = atoi(argv[i + 1]); + info.srWindow.Top = atoi(argv[i + 2]); + i += 3; + info.srWindow.Right = info.srWindow.Left + dx; + info.srWindow.Bottom = info.srWindow.Top + dy; + change = true; + } else if (arg == "-win" && (i + 2) < argc) { + info.srWindow.Right = info.srWindow.Left + atoi(argv[i + 1]) - 1; + info.srWindow.Bottom = info.srWindow.Top + atoi(argv[i + 2]) - 1; + i += 3; + change = true; + } else if (arg == "-set") { + change = true; + ++i; + } else if (arg == "--help" || arg == "-help") { + usage(); + exit(0); + } else { + fprintf(stderr, "error: unrecognized argument: %s\n", arg.c_str()); + usage(); + exit(1); + } + } + + if (change) { + success = SetConsoleScreenBufferInfoEx(conout, &info); + if (success) { + printf("success\n"); + } else { + printf("SetConsoleScreenBufferInfoEx call failed\n"); + } + success = GetConsoleScreenBufferInfoEx(conout, &info); + ASSERT(success && "GetConsoleScreenBufferInfoEx failed"); + } + + auto dump = [](const char *fmt, ...) { + char msg[256]; + va_list ap; + va_start(ap, fmt); + vsprintf(msg, fmt, ap); + va_end(ap); + trace("%s", msg); + printf("%s\n", msg); + }; + + dump("buffer-size: %d x %d", info.dwSize.X, info.dwSize.Y); + dump("window-size: %d x %d", + info.srWindow.Right - info.srWindow.Left + 1, + info.srWindow.Bottom - info.srWindow.Top + 1); + dump("window-pos: %d, %d", info.srWindow.Left, info.srWindow.Top); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/SetBufferSize.cc b/src/libs/3rdparty/winpty/misc/SetBufferSize.cc new file mode 100644 index 0000000000..b50a1f8dc3 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/SetBufferSize.cc @@ -0,0 +1,32 @@ +#include <windows.h> + +#include <stdio.h> +#include <stdlib.h> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + if (argc != 3) { + printf("Usage: %s x y width height\n", argv[0]); + return 1; + } + + const HANDLE conout = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + ASSERT(conout != INVALID_HANDLE_VALUE); + + COORD size = { + (short)atoi(argv[1]), + (short)atoi(argv[2]), + }; + + BOOL ret = SetConsoleScreenBufferSize(conout, size); + const unsigned lastError = GetLastError(); + const char *const retStr = ret ? "OK" : "failed"; + trace("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)", retStr, lastError); + printf("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)\n", retStr, lastError); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/SetCursorPos.cc b/src/libs/3rdparty/winpty/misc/SetCursorPos.cc new file mode 100644 index 0000000000..d20fdbdfc0 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/SetCursorPos.cc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + int col = atoi(argv[1]); + int row = atoi(argv[2]); + setCursorPos(col, row); + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/SetFont.cc b/src/libs/3rdparty/winpty/misc/SetFont.cc new file mode 100644 index 0000000000..9bcd4b4cc9 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/SetFont.cc @@ -0,0 +1,145 @@ +#include <windows.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string> + +#include "TestUtil.cc" + +#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0])) + +// See https://en.wikipedia.org/wiki/List_of_CJK_fonts +const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese +const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese +const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese +const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean + +int main() { + setlocale(LC_ALL, ""); + wchar_t *cmdline = GetCommandLineW(); + int argc = 0; + wchar_t **argv = CommandLineToArgvW(cmdline, &argc); + const HANDLE conout = openConout(); + + if (argc == 1) { + cprintf(L"Usage:\n"); + cprintf(L" SetFont <index>\n"); + cprintf(L" SetFont options\n"); + cprintf(L"\n"); + cprintf(L"Options for SetCurrentConsoleFontEx:\n"); + cprintf(L" -idx INDEX\n"); + cprintf(L" -w WIDTH\n"); + cprintf(L" -h HEIGHT\n"); + cprintf(L" -family (0xNN|NN)\n"); + cprintf(L" -weight (normal|bold|NNN)\n"); + cprintf(L" -face FACENAME\n"); + cprintf(L" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n"); + cprintf(L" -tt\n"); + cprintf(L" -vec\n"); + cprintf(L" -vp\n"); + cprintf(L" -dev\n"); + cprintf(L" -roman\n"); + cprintf(L" -swiss\n"); + cprintf(L" -modern\n"); + cprintf(L" -script\n"); + cprintf(L" -decorative\n"); + return 0; + } + + if (isdigit(argv[1][0])) { + int index = _wtoi(argv[1]); + HMODULE kernel32 = LoadLibraryW(L"kernel32.dll"); + FARPROC proc = GetProcAddress(kernel32, "SetConsoleFont"); + if (proc == NULL) { + cprintf(L"Couldn't get address of SetConsoleFont\n"); + } else { + BOOL ret = reinterpret_cast<BOOL WINAPI(*)(HANDLE, DWORD)>(proc)( + conout, index); + cprintf(L"SetFont returned %d\n", ret); + } + return 0; + } + + CONSOLE_FONT_INFOEX fontex = {0}; + fontex.cbSize = sizeof(fontex); + + for (int i = 1; i < argc; ++i) { + std::wstring arg = argv[i]; + if (i + 1 < argc) { + std::wstring next = argv[i + 1]; + if (arg == L"-idx") { + fontex.nFont = _wtoi(next.c_str()); + ++i; continue; + } else if (arg == L"-w") { + fontex.dwFontSize.X = _wtoi(next.c_str()); + ++i; continue; + } else if (arg == L"-h") { + fontex.dwFontSize.Y = _wtoi(next.c_str()); + ++i; continue; + } else if (arg == L"-weight") { + if (next == L"normal") { + fontex.FontWeight = 400; + } else if (next == L"bold") { + fontex.FontWeight = 700; + } else { + fontex.FontWeight = _wtoi(next.c_str()); + } + ++i; continue; + } else if (arg == L"-face") { + wcsncpy(fontex.FaceName, next.c_str(), COUNT_OF(fontex.FaceName)); + ++i; continue; + } else if (arg == L"-family") { + fontex.FontFamily = strtol(narrowString(next).c_str(), nullptr, 0); + ++i; continue; + } + } + if (arg == L"-tt") { + fontex.FontFamily |= TMPF_TRUETYPE; + } else if (arg == L"-vec") { + fontex.FontFamily |= TMPF_VECTOR; + } else if (arg == L"-vp") { + // Setting the TMPF_FIXED_PITCH bit actually indicates variable + // pitch. + fontex.FontFamily |= TMPF_FIXED_PITCH; + } else if (arg == L"-dev") { + fontex.FontFamily |= TMPF_DEVICE; + } else if (arg == L"-roman") { + fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_ROMAN; + } else if (arg == L"-swiss") { + fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_SWISS; + } else if (arg == L"-modern") { + fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_MODERN; + } else if (arg == L"-script") { + fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_SCRIPT; + } else if (arg == L"-decorative") { + fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_DECORATIVE; + } else if (arg == L"-face-gothic") { + wcsncpy(fontex.FaceName, kMSGothic, COUNT_OF(fontex.FaceName)); + } else if (arg == L"-face-simsun") { + wcsncpy(fontex.FaceName, kNSimSun, COUNT_OF(fontex.FaceName)); + } else if (arg == L"-face-minglight") { + wcsncpy(fontex.FaceName, kMingLight, COUNT_OF(fontex.FaceName)); + } else if (arg == L"-face-gulimche") { + wcsncpy(fontex.FaceName, kGulimChe, COUNT_OF(fontex.FaceName)); + } else { + cprintf(L"Unrecognized argument: %ls\n", arg.c_str()); + exit(1); + } + } + + cprintf(L"Setting to: nFont=%u dwFontSize=(%d,%d) " + L"FontFamily=0x%x FontWeight=%u " + L"FaceName=\"%ls\"\n", + static_cast<unsigned>(fontex.nFont), + fontex.dwFontSize.X, fontex.dwFontSize.Y, + fontex.FontFamily, fontex.FontWeight, + fontex.FaceName); + + BOOL ret = SetCurrentConsoleFontEx( + conout, + FALSE, + &fontex); + cprintf(L"SetCurrentConsoleFontEx returned %d\n", ret); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/SetWindowRect.cc b/src/libs/3rdparty/winpty/misc/SetWindowRect.cc new file mode 100644 index 0000000000..6291dd6745 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/SetWindowRect.cc @@ -0,0 +1,36 @@ +#include <windows.h> + +#include <stdio.h> +#include <stdlib.h> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + if (argc != 5) { + printf("Usage: %s x y width height\n", argv[0]); + return 1; + } + + const HANDLE conout = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + ASSERT(conout != INVALID_HANDLE_VALUE); + + SMALL_RECT sr = { + (short)atoi(argv[1]), + (short)atoi(argv[2]), + (short)(atoi(argv[1]) + atoi(argv[3]) - 1), + (short)(atoi(argv[2]) + atoi(argv[4]) - 1), + }; + + trace("Calling SetConsoleWindowInfo with {L=%d,T=%d,R=%d,B=%d}", + sr.Left, sr.Top, sr.Right, sr.Bottom); + BOOL ret = SetConsoleWindowInfo(conout, TRUE, &sr); + const unsigned lastError = GetLastError(); + const char *const retStr = ret ? "OK" : "failed"; + trace("SetConsoleWindowInfo ret: %s (LastError=0x%x)", retStr, lastError); + printf("SetConsoleWindowInfo ret: %s (LastError=0x%x)\n", retStr, lastError); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ShowArgv.cc b/src/libs/3rdparty/winpty/misc/ShowArgv.cc new file mode 100644 index 0000000000..29a0f09131 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ShowArgv.cc @@ -0,0 +1,12 @@ +// This test program is useful for studying commandline<->argv conversion. + +#include <stdio.h> +#include <windows.h> + +int main(int argc, char **argv) +{ + printf("cmdline = [%s]\n", GetCommandLine()); + for (int i = 0; i < argc; ++i) + printf("[%s]\n", argv[i]); + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc b/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc new file mode 100644 index 0000000000..75fbfb81f1 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc @@ -0,0 +1,40 @@ +#include <windows.h> +#include <stdio.h> +#include <ctype.h> + +int main(int argc, char *argv[]) +{ + static int escCount = 0; + + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + while (true) { + DWORD count; + INPUT_RECORD ir; + if (!ReadConsoleInput(hStdin, &ir, 1, &count)) { + printf("ReadConsoleInput failed\n"); + return 1; + } + + if (true) { + DWORD mode; + GetConsoleMode(hStdin, &mode); + SetConsoleMode(hStdin, mode & ~ENABLE_PROCESSED_INPUT); + } + + if (ir.EventType == KEY_EVENT) { + const KEY_EVENT_RECORD &ker = ir.Event.KeyEvent; + printf("%s", ker.bKeyDown ? "dn" : "up"); + printf(" ch="); + if (isprint(ker.uChar.AsciiChar)) + printf("'%c'", ker.uChar.AsciiChar); + printf("%d", ker.uChar.AsciiChar); + printf(" vk=%#x", ker.wVirtualKeyCode); + printf(" scan=%#x", ker.wVirtualScanCode); + printf(" state=%#x", (int)ker.dwControlKeyState); + printf(" repeat=%d", ker.wRepeatCount); + printf("\n"); + if (ker.uChar.AsciiChar == 27 && ++escCount == 6) + break; + } + } +} diff --git a/src/libs/3rdparty/winpty/misc/Spew.py b/src/libs/3rdparty/winpty/misc/Spew.py new file mode 100644 index 0000000000..9d1796af37 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Spew.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +i = 0; +while True: + i += 1 + print(i) diff --git a/src/libs/3rdparty/winpty/misc/TestUtil.cc b/src/libs/3rdparty/winpty/misc/TestUtil.cc new file mode 100644 index 0000000000..c832a12b85 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/TestUtil.cc @@ -0,0 +1,172 @@ +// This file is included into test programs using #include + +#include <windows.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <wchar.h> +#include <vector> +#include <string> + +#include "../src/shared/DebugClient.h" +#include "../src/shared/TimeMeasurement.h" + +#include "../src/shared/DebugClient.cc" +#include "../src/shared/WinptyAssert.cc" +#include "../src/shared/WinptyException.cc" + +// Launch this test program again, in a new console that we will destroy. +static void startChildProcess(const wchar_t *args) { + wchar_t program[1024]; + wchar_t cmdline[1024]; + GetModuleFileNameW(NULL, program, 1024); + swprintf(cmdline, L"\"%ls\" %ls", program, args); + + STARTUPINFOW sui; + PROCESS_INFORMATION pi; + memset(&sui, 0, sizeof(sui)); + memset(&pi, 0, sizeof(pi)); + sui.cb = sizeof(sui); + + CreateProcessW(program, cmdline, + NULL, NULL, + /*bInheritHandles=*/FALSE, + /*dwCreationFlags=*/CREATE_NEW_CONSOLE, + NULL, NULL, + &sui, &pi); +} + +static void setBufferSize(HANDLE conout, int x, int y) { + COORD size = { static_cast<SHORT>(x), static_cast<SHORT>(y) }; + BOOL success = SetConsoleScreenBufferSize(conout, size); + trace("setBufferSize: (%d,%d), result=%d", x, y, success); +} + +static void setWindowPos(HANDLE conout, int x, int y, int w, int h) { + SMALL_RECT r = { + static_cast<SHORT>(x), static_cast<SHORT>(y), + static_cast<SHORT>(x + w - 1), + static_cast<SHORT>(y + h - 1) + }; + BOOL success = SetConsoleWindowInfo(conout, /*bAbsolute=*/TRUE, &r); + trace("setWindowPos: (%d,%d,%d,%d), result=%d", x, y, w, h, success); +} + +static void setCursorPos(HANDLE conout, int x, int y) { + COORD coord = { static_cast<SHORT>(x), static_cast<SHORT>(y) }; + SetConsoleCursorPosition(conout, coord); +} + +static void setBufferSize(int x, int y) { + setBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), x, y); +} + +static void setWindowPos(int x, int y, int w, int h) { + setWindowPos(GetStdHandle(STD_OUTPUT_HANDLE), x, y, w, h); +} + +static void setCursorPos(int x, int y) { + setCursorPos(GetStdHandle(STD_OUTPUT_HANDLE), x, y); +} + +static void countDown(int sec) { + for (int i = sec; i > 0; --i) { + printf("%d.. ", i); + fflush(stdout); + Sleep(1000); + } + printf("\n"); +} + +static void writeBox(int x, int y, int w, int h, char ch, int attributes=7) { + CHAR_INFO info = { 0 }; + info.Char.AsciiChar = ch; + info.Attributes = attributes; + std::vector<CHAR_INFO> buf(w * h, info); + HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + COORD bufSize = { static_cast<SHORT>(w), static_cast<SHORT>(h) }; + COORD bufCoord = { 0, 0 }; + SMALL_RECT writeRegion = { + static_cast<SHORT>(x), + static_cast<SHORT>(y), + static_cast<SHORT>(x + w - 1), + static_cast<SHORT>(y + h - 1) + }; + WriteConsoleOutputA(conout, buf.data(), bufSize, bufCoord, &writeRegion); +} + +static void setChar(int x, int y, char ch, int attributes=7) { + writeBox(x, y, 1, 1, ch, attributes); +} + +static void fillChar(int x, int y, int repeat, char ch) { + COORD coord = { static_cast<SHORT>(x), static_cast<SHORT>(y) }; + DWORD actual = 0; + FillConsoleOutputCharacterA( + GetStdHandle(STD_OUTPUT_HANDLE), + ch, repeat, coord, &actual); +} + +static void repeatChar(int count, char ch) { + for (int i = 0; i < count; ++i) { + putchar(ch); + } + fflush(stdout); +} + +// I don't know why, but wprintf fails to print this face name, +// "MS ゴシック" (aka MS Gothic). It helps to use wprintf instead of printf, and +// it helps to call `setlocale(LC_ALL, "")`, but the Japanese symbols are +// ultimately converted to `?` symbols, even though MS Gothic is able to +// display its own name, and the current code page is 932 (Shift-JIS). +static void cvfprintf(HANDLE conout, const wchar_t *fmt, va_list ap) { + wchar_t buffer[256]; + vswprintf(buffer, 256 - 1, fmt, ap); + buffer[255] = L'\0'; + DWORD actual = 0; + if (!WriteConsoleW(conout, buffer, wcslen(buffer), &actual, NULL)) { + wprintf(L"WriteConsoleW call failed!\n"); + } +} + +static void cfprintf(HANDLE conout, const wchar_t *fmt, ...) { + va_list ap; + va_start(ap, fmt); + cvfprintf(conout, fmt, ap); + va_end(ap); +} + +static void cprintf(const wchar_t *fmt, ...) { + va_list ap; + va_start(ap, fmt); + cvfprintf(GetStdHandle(STD_OUTPUT_HANDLE), fmt, ap); + va_end(ap); +} + +static std::string narrowString(const std::wstring &input) +{ + int mblen = WideCharToMultiByte( + CP_UTF8, 0, + input.data(), input.size(), + NULL, 0, NULL, NULL); + if (mblen <= 0) { + return std::string(); + } + std::vector<char> tmp(mblen); + int mblen2 = WideCharToMultiByte( + CP_UTF8, 0, + input.data(), input.size(), + tmp.data(), tmp.size(), + NULL, NULL); + assert(mblen2 == mblen); + return std::string(tmp.data(), tmp.size()); +} + +HANDLE openConout() { + const HANDLE conout = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + ASSERT(conout != INVALID_HANDLE_VALUE); + return conout; +} diff --git a/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc b/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc new file mode 100644 index 0000000000..7210d41032 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc @@ -0,0 +1,102 @@ +// Demonstrates how U+30FC is sometimes handled as a single-width character +// when it should be handled as a double-width character. +// +// It only runs on computers where 932 is a valid code page. Set the system +// local to "Japanese (Japan)" to ensure this. +// +// The problem seems to happen when U+30FC is printed in a console using the +// Lucida Console font, and only when that font is at certain sizes. +// + +#include <windows.h> +#include <assert.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "TestUtil.cc" + +#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0])) + +static void setFont(const wchar_t *faceName, int pxSize) { + CONSOLE_FONT_INFOEX infoex = {0}; + infoex.cbSize = sizeof(infoex); + infoex.dwFontSize.Y = pxSize; + wcsncpy(infoex.FaceName, faceName, COUNT_OF(infoex.FaceName)); + BOOL ret = SetCurrentConsoleFontEx( + GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &infoex); + assert(ret); +} + +static bool performTest(const wchar_t testChar) { + const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + + SetConsoleTextAttribute(conout, 7); + + system("cls"); + DWORD actual = 0; + BOOL ret = WriteConsoleW(conout, &testChar, 1, &actual, NULL); + assert(ret && actual == 1); + + CHAR_INFO verify[2]; + COORD bufSize = {2, 1}; + COORD bufCoord = {0, 0}; + const SMALL_RECT readRegion = {0, 0, 1, 0}; + SMALL_RECT actualRegion = readRegion; + ret = ReadConsoleOutputW(conout, verify, bufSize, bufCoord, &actualRegion); + assert(ret && !memcmp(&readRegion, &actualRegion, sizeof(readRegion))); + assert(verify[0].Char.UnicodeChar == testChar); + + if (verify[1].Char.UnicodeChar == testChar) { + // Typical double-width behavior with a TrueType font. Pass. + assert(verify[0].Attributes == 0x107); + assert(verify[1].Attributes == 0x207); + return true; + } else if (verify[1].Char.UnicodeChar == 0) { + // Typical double-width behavior with a Raster Font. Pass. + assert(verify[0].Attributes == 7); + assert(verify[1].Attributes == 0); + return true; + } else if (verify[1].Char.UnicodeChar == L' ') { + // Single-width behavior. Fail. + assert(verify[0].Attributes == 7); + assert(verify[1].Attributes == 7); + return false; + } else { + // Unexpected output. + assert(false); + } +} + +int main(int argc, char *argv[]) { + setlocale(LC_ALL, ""); + if (argc == 1) { + startChildProcess(L"CHILD"); + return 0; + } + + assert(SetConsoleCP(932)); + assert(SetConsoleOutputCP(932)); + + const wchar_t testChar = 0x30FC; + const wchar_t *const faceNames[] = { + L"Lucida Console", + L"Consolas", + L"MS ゴシック", + }; + + trace("Test started"); + + for (auto faceName : faceNames) { + for (int px = 1; px <= 50; ++px) { + setFont(faceName, px); + if (!performTest(testChar)) { + trace("FAILURE: %s %dpx", narrowString(faceName).c_str(), px); + } + } + } + + trace("Test complete"); + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc b/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc new file mode 100644 index 0000000000..a8d798e70d --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc @@ -0,0 +1,246 @@ +#include <windows.h> + +#include <assert.h> +#include <vector> + +#include "TestUtil.cc" + +#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0])) + + +CHAR_INFO ci(wchar_t ch, WORD attributes) { + CHAR_INFO ret; + ret.Char.UnicodeChar = ch; + ret.Attributes = attributes; + return ret; +} + +CHAR_INFO ci(wchar_t ch) { + return ci(ch, 7); +} + +CHAR_INFO ci() { + return ci(L' '); +} + +bool operator==(SMALL_RECT x, SMALL_RECT y) { + return !memcmp(&x, &y, sizeof(x)); +} + +SMALL_RECT sr(COORD pt, COORD size) { + return { + pt.X, pt.Y, + static_cast<SHORT>(pt.X + size.X - 1), + static_cast<SHORT>(pt.Y + size.Y - 1) + }; +} + +static void set( + const COORD pt, + const COORD size, + const std::vector<CHAR_INFO> &data) { + assert(data.size() == size.X * size.Y); + SMALL_RECT writeRegion = sr(pt, size); + BOOL ret = WriteConsoleOutputW( + GetStdHandle(STD_OUTPUT_HANDLE), + data.data(), size, {0, 0}, &writeRegion); + assert(ret && writeRegion == sr(pt, size)); +} + +static void set( + const COORD pt, + const std::vector<CHAR_INFO> &data) { + set(pt, {static_cast<SHORT>(data.size()), 1}, data); +} + +static void writeAttrsAt( + const COORD pt, + const std::vector<WORD> &data) { + DWORD actual = 0; + BOOL ret = WriteConsoleOutputAttribute( + GetStdHandle(STD_OUTPUT_HANDLE), + data.data(), data.size(), pt, &actual); + assert(ret && actual == data.size()); +} + +static void writeCharsAt( + const COORD pt, + const std::vector<wchar_t> &data) { + DWORD actual = 0; + BOOL ret = WriteConsoleOutputCharacterW( + GetStdHandle(STD_OUTPUT_HANDLE), + data.data(), data.size(), pt, &actual); + assert(ret && actual == data.size()); +} + +static void writeChars( + const std::vector<wchar_t> &data) { + DWORD actual = 0; + BOOL ret = WriteConsoleW( + GetStdHandle(STD_OUTPUT_HANDLE), + data.data(), data.size(), &actual, NULL); + assert(ret && actual == data.size()); +} + +std::vector<CHAR_INFO> get( + const COORD pt, + const COORD size) { + std::vector<CHAR_INFO> data(size.X * size.Y); + SMALL_RECT readRegion = sr(pt, size); + BOOL ret = ReadConsoleOutputW( + GetStdHandle(STD_OUTPUT_HANDLE), + data.data(), size, {0, 0}, &readRegion); + assert(ret && readRegion == sr(pt, size)); + return data; +} + +std::vector<wchar_t> readCharsAt( + const COORD pt, + int size) { + std::vector<wchar_t> data(size); + DWORD actual = 0; + BOOL ret = ReadConsoleOutputCharacterW( + GetStdHandle(STD_OUTPUT_HANDLE), + data.data(), data.size(), pt, &actual); + assert(ret); + data.resize(actual); // With double-width chars, we can read fewer than `size`. + return data; +} + +static void dump(const COORD pt, const COORD size) { + for (CHAR_INFO ci : get(pt, size)) { + printf("%04X %04X\n", ci.Char.UnicodeChar, ci.Attributes); + } +} + +static void dumpCharsAt(const COORD pt, int size) { + for (wchar_t ch : readCharsAt(pt, size)) { + printf("%04X\n", ch); + } +} + +static COORD getCursorPos() { + CONSOLE_SCREEN_BUFFER_INFO info = { sizeof(info) }; + assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info)); + return info.dwCursorPosition; +} + +static void test1() { + // We write "䀀䀀", then write "䀁" in the middle of the two. The second + // write turns the first and last cells into spaces. The LEADING/TRAILING + // flags retain consistency. + printf("test1 - overlap full-width char with full-width char\n"); + writeCharsAt({1,0}, {0x4000, 0x4000}); + dump({0,0}, {6,1}); + printf("\n"); + writeCharsAt({2,0}, {0x4001}); + dump({0,0}, {6,1}); + printf("\n"); +} + +static void test2() { + // Like `test1`, but use a lower-level API to do the write. Consistency is + // preserved here too -- the first and last cells are replaced with spaces. + printf("test2 - overlap full-width char with full-width char (lowlevel)\n"); + writeCharsAt({1,0}, {0x4000, 0x4000}); + dump({0,0}, {6,1}); + printf("\n"); + set({2,0}, {ci(0x4001,0x107), ci(0x4001,0x207)}); + dump({0,0}, {6,1}); + printf("\n"); +} + +static void test3() { + // However, the lower-level API can break the LEADING/TRAILING invariant + // explicitly: + printf("test3 - explicitly violate LEADING/TRAILING using lowlevel API\n"); + set({1,0}, { + ci(0x4000, 0x207), + ci(0x4001, 0x107), + ci(0x3044, 7), + ci(L'X', 0x107), + ci(L'X', 0x207), + }); + dump({0,0}, {7,1}); +} + +static void test4() { + // It is possible for the two cells of a double-width character to have two + // colors. + printf("test4 - use lowlevel to assign two colors to one full-width char\n"); + set({0,0}, { + ci(0x4000, 0x142), + ci(0x4000, 0x224), + }); + dump({0,0}, {2,1}); +} + +static void test5() { + // WriteConsoleOutputAttribute doesn't seem to affect the LEADING/TRAILING + // flags. + printf("test5 - WriteConsoleOutputAttribute cannot affect LEADING/TRAILING\n"); + + // Trying to clear the flags doesn't work... + writeCharsAt({0,0}, {0x4000}); + dump({0,0}, {2,1}); + writeAttrsAt({0,0}, {0x42, 0x24}); + printf("\n"); + dump({0,0}, {2,1}); + + // ... and trying to add them also doesn't work. + writeCharsAt({0,1}, {'A', ' '}); + writeAttrsAt({0,1}, {0x107, 0x207}); + printf("\n"); + dump({0,1}, {2,1}); +} + +static void test6() { + // The cursor position may be on either cell of a double-width character. + // Visually, the cursor appears under both cells, regardless of which + // specific one has the cursor. + printf("test6 - cursor can be either left or right cell of full-width char\n"); + + writeCharsAt({2,1}, {0x4000}); + + setCursorPos(2, 1); + auto pos1 = getCursorPos(); + Sleep(1000); + + setCursorPos(3, 1); + auto pos2 = getCursorPos(); + Sleep(1000); + + setCursorPos(0, 15); + printf("%d,%d\n", pos1.X, pos1.Y); + printf("%d,%d\n", pos2.X, pos2.Y); +} + +static void runTest(void (&test)()) { + system("cls"); + setCursorPos(0, 14); + test(); + system("pause"); +} + +int main(int argc, char *argv[]) { + if (argc == 1) { + startChildProcess(L"CHILD"); + return 0; + } + + setWindowPos(0, 0, 1, 1); + setBufferSize(80, 40); + setWindowPos(0, 0, 80, 40); + + auto cp = GetConsoleOutputCP(); + assert(cp == 932 || cp == 936 || cp == 949 || cp == 950); + + runTest(test1); + runTest(test2); + runTest(test3); + runTest(test4); + runTest(test5); + runTest(test6); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc b/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc new file mode 100644 index 0000000000..05f80f70bd --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc @@ -0,0 +1,130 @@ +// +// Test half-width vs full-width characters. +// + +#include <windows.h> +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include "TestUtil.cc" + +static void writeChars(const wchar_t *text) { + wcslen(text); + const int len = wcslen(text); + DWORD actual = 0; + BOOL ret = WriteConsoleW( + GetStdHandle(STD_OUTPUT_HANDLE), + text, len, &actual, NULL); + trace("writeChars: ret=%d, actual=%lld", ret, (long long)actual); +} + +static void dumpChars(int x, int y, int w, int h) { + BOOL ret; + const COORD bufSize = {w, h}; + const COORD bufCoord = {0, 0}; + const SMALL_RECT topLeft = {x, y, x + w - 1, y + h - 1}; + CHAR_INFO mbcsData[w * h]; + CHAR_INFO unicodeData[w * h]; + SMALL_RECT readRegion; + readRegion = topLeft; + ret = ReadConsoleOutputW(GetStdHandle(STD_OUTPUT_HANDLE), unicodeData, + bufSize, bufCoord, &readRegion); + assert(ret); + readRegion = topLeft; + ret = ReadConsoleOutputA(GetStdHandle(STD_OUTPUT_HANDLE), mbcsData, + bufSize, bufCoord, &readRegion); + assert(ret); + + printf("\n"); + for (int i = 0; i < w * h; ++i) { + printf("(%02d,%02d) CHAR: %04x %4x -- %02x %4x\n", + x + i % w, y + i / w, + (unsigned short)unicodeData[i].Char.UnicodeChar, + (unsigned short)unicodeData[i].Attributes, + (unsigned char)mbcsData[i].Char.AsciiChar, + (unsigned short)mbcsData[i].Attributes); + } +} + +int main(int argc, char *argv[]) { + system("cls"); + setWindowPos(0, 0, 1, 1); + setBufferSize(80, 38); + setWindowPos(0, 0, 80, 38); + + // Write text. + const wchar_t text1[] = { + 0x3044, // U+3044 (HIRAGANA LETTER I) + 0x2014, // U+2014 (EM DASH) + 0x3044, // U+3044 (HIRAGANA LETTER I) + 0xFF2D, // U+FF2D (FULLWIDTH LATIN CAPITAL LETTER M) + 0x30FC, // U+30FC (KATAKANA-HIRAGANA PROLONGED SOUND MARK) + 0x0031, // U+3031 (DIGIT ONE) + 0x2014, // U+2014 (EM DASH) + 0x0032, // U+0032 (DIGIT TWO) + 0x005C, // U+005C (REVERSE SOLIDUS) + 0x3044, // U+3044 (HIRAGANA LETTER I) + 0 + }; + setCursorPos(0, 0); + writeChars(text1); + + setCursorPos(78, 1); + writeChars(L"<>"); + + const wchar_t text2[] = { + 0x0032, // U+3032 (DIGIT TWO) + 0x3044, // U+3044 (HIRAGANA LETTER I) + 0, + }; + setCursorPos(78, 1); + writeChars(text2); + + system("pause"); + + dumpChars(0, 0, 17, 1); + dumpChars(2, 0, 2, 1); + dumpChars(2, 0, 1, 1); + dumpChars(3, 0, 1, 1); + dumpChars(78, 1, 2, 1); + dumpChars(0, 2, 2, 1); + + system("pause"); + system("cls"); + + const wchar_t text3[] = { + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 1 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 2 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 3 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 4 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 5 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 6 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 7 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 8 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 9 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 10 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 11 + 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 12 + L'\r', '\n', + L'\r', '\n', + 0 + }; + writeChars(text3); + system("pause"); + { + const COORD bufSize = {80, 2}; + const COORD bufCoord = {0, 0}; + SMALL_RECT readRegion = {0, 0, 79, 1}; + CHAR_INFO unicodeData[160]; + BOOL ret = ReadConsoleOutputW(GetStdHandle(STD_OUTPUT_HANDLE), unicodeData, + bufSize, bufCoord, &readRegion); + assert(ret); + for (int i = 0; i < 96; ++i) { + printf("%04x ", unicodeData[i].Char.UnicodeChar); + } + printf("\n"); + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/UnixEcho.cc b/src/libs/3rdparty/winpty/misc/UnixEcho.cc new file mode 100644 index 0000000000..372e045157 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/UnixEcho.cc @@ -0,0 +1,89 @@ +/* + * Unix test code that puts the terminal into raw mode, then echos typed + * characters to stdout. Derived from sample code in the Stevens book, posted + * online at http://www.lafn.org/~dave/linux/terminalIO.html. + */ + +#include <termios.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include "FormatChar.h" + +static struct termios save_termios; +static int term_saved; + +/* RAW! mode */ +int tty_raw(int fd) +{ + struct termios buf; + + if (tcgetattr(fd, &save_termios) < 0) /* get the original state */ + return -1; + + buf = save_termios; + + /* echo off, canonical mode off, extended input + processing off, signal chars off */ + buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + + /* no SIGINT on BREAK, CR-to-NL off, input parity + check off, don't strip the 8th bit on input, + ouput flow control off */ + buf.c_iflag &= ~(BRKINT | ICRNL | ISTRIP | IXON); + + /* clear size bits, parity checking off */ + buf.c_cflag &= ~(CSIZE | PARENB); + + /* set 8 bits/char */ + buf.c_cflag |= CS8; + + /* output processing off */ + buf.c_oflag &= ~(OPOST); + + buf.c_cc[VMIN] = 1; /* 1 byte at a time */ + buf.c_cc[VTIME] = 0; /* no timer on input */ + + if (tcsetattr(fd, TCSAFLUSH, &buf) < 0) + return -1; + + term_saved = 1; + + return 0; +} + + +/* set it to normal! */ +int tty_reset(int fd) +{ + if (term_saved) + if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0) + return -1; + + return 0; +} + + +int main() +{ + tty_raw(0); + + int count = 0; + while (true) { + char ch; + char buf[16]; + int actual = read(0, &ch, 1); + if (actual != 1) { + perror("read error"); + break; + } + formatChar(buf, ch); + fputs(buf, stdout); + fflush(stdout); + if (ch == 3) // Ctrl-C + break; + } + + tty_reset(0); + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Utf16Echo.cc b/src/libs/3rdparty/winpty/misc/Utf16Echo.cc new file mode 100644 index 0000000000..ef5f302de4 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Utf16Echo.cc @@ -0,0 +1,46 @@ +#include <windows.h> +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include <vector> +#include <string> + +int main(int argc, char *argv[]) { + system("cls"); + + if (argc == 1) { + printf("Usage: %s hhhh\n", argv[0]); + return 0; + } + + std::wstring dataToWrite; + for (int i = 1; i < argc; ++i) { + wchar_t ch = strtol(argv[i], NULL, 16); + dataToWrite.push_back(ch); + } + + DWORD actual = 0; + BOOL ret = WriteConsoleW( + GetStdHandle(STD_OUTPUT_HANDLE), + dataToWrite.data(), dataToWrite.size(), &actual, NULL); + assert(ret && actual == dataToWrite.size()); + + // Read it back. + std::vector<CHAR_INFO> readBuffer(dataToWrite.size() * 2); + COORD bufSize = {static_cast<short>(readBuffer.size()), 1}; + COORD bufCoord = {0, 0}; + SMALL_RECT topLeft = {0, 0, static_cast<short>(readBuffer.size() - 1), 0}; + ret = ReadConsoleOutputW( + GetStdHandle(STD_OUTPUT_HANDLE), readBuffer.data(), + bufSize, bufCoord, &topLeft); + assert(ret); + + printf("\n"); + for (int i = 0; i < readBuffer.size(); ++i) { + printf("CHAR: %04x %04x\n", + readBuffer[i].Char.UnicodeChar, + readBuffer[i].Attributes); + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc b/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc new file mode 100644 index 0000000000..58f0897022 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc @@ -0,0 +1,122 @@ +// +// 2015-09-25 +// I measured these limits on the size of a single ReadConsoleOutputW call. +// The limit seems to more-or-less disppear with Windows 8, which is the first +// OS to stop using ALPCs for console I/O. My guess is that the new I/O +// method does not use the 64KiB shared memory buffer that the ALPC method +// uses. +// +// I'm guessing the remaining difference between Windows 8/8.1 and Windows 10 +// might be related to the 32-vs-64-bitness. +// +// Client OSs +// +// Windows XP 32-bit VM ==> up to 13304 characters +// - 13304x1 works, but 13305x1 fails instantly +// Windows 7 32-bit VM ==> between 16-17 thousand characters +// - 16000x1 works, 17000x1 fails instantly +// - 163x100 *crashes* conhost.exe but leaves VeryLargeRead.exe running +// Windows 8 32-bit VM ==> between 240-250 million characters +// - 10000x24000 works, but 10000x25000 does not +// Windows 8.1 32-bit VM ==> between 240-250 million characters +// - 10000x24000 works, but 10000x25000 does not +// Windows 10 64-bit VM ==> no limit (tested to 576 million characters) +// - 24000x24000 works +// - `ver` reports [Version 10.0.10240], conhost.exe and ConhostV1.dll are +// 10.0.10240.16384 for file and product version. ConhostV2.dll is +// 10.0.10240.16391 for file and product version. +// +// Server OSs +// +// Windows Server 2008 64-bit VM ==> 14300-14400 characters +// - 14300x1 works, 14400x1 fails instantly +// - This OS does not have conhost.exe. +// - `ver` reports [Version 6.0.6002] +// Windows Server 2008 R2 64-bit VM ==> 15600-15700 characters +// - 15600x1 works, 15700x1 fails instantly +// - This OS has conhost.exe, and procexp.exe reveals console ALPC ports in +// use in conhost.exe. +// - `ver` reports [Version 6.1.7601], conhost.exe is 6.1.7601.23153 for file +// and product version. +// Windows Server 2012 64-bit VM ==> at least 100 million characters +// - 10000x10000 works (VM had only 1GiB of RAM, so I skipped larger tests) +// - This OS has Windows 8's task manager and procexp.exe reveals the same +// lack of ALPC ports and the same \Device\ConDrv\* files as Windows 8. +// - `ver` reports [Version 6.2.9200], conhost.exe is 6.2.9200.16579 for file +// and product version. +// +// To summarize: +// +// client-OS server-OS notes +// --------------------------------------------------------------------------- +// XP Server 2008 CSRSS, small reads +// 7 Server 2008 R2 ALPC-to-conhost, small reads +// 8, 8.1 Server 2012 new I/O interface, large reads allowed +// 10 <no server OS yet> enhanced console w/rewrapping +// +// (Presumably, Win2K, Vista, and Win2K3 behave the same as XP. conhost.exe +// was announced as a Win7 feature.) +// + +#include <windows.h> +#include <assert.h> +#include <vector> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + long long width = 9000; + long long height = 9000; + + assert(argc >= 1); + if (argc == 4) { + width = atoi(argv[2]); + height = atoi(argv[3]); + } else { + if (argc == 3) { + width = atoi(argv[1]); + height = atoi(argv[2]); + } + wchar_t args[1024]; + swprintf(args, 1024, L"CHILD %lld %lld", width, height); + startChildProcess(args); + return 0; + } + + const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + + setWindowPos(0, 0, 1, 1); + setBufferSize(width, height); + setWindowPos(0, 0, std::min(80LL, width), std::min(50LL, height)); + + setCursorPos(0, 0); + printf("A"); + fflush(stdout); + setCursorPos(width - 2, height - 1); + printf("B"); + fflush(stdout); + + trace("sizeof(CHAR_INFO) = %d", (int)sizeof(CHAR_INFO)); + + trace("Allocating buffer..."); + CHAR_INFO *buffer = new CHAR_INFO[width * height]; + assert(buffer != NULL); + memset(&buffer[0], 0, sizeof(CHAR_INFO)); + memset(&buffer[width * height - 2], 0, sizeof(CHAR_INFO)); + + COORD bufSize = { width, height }; + COORD bufCoord = { 0, 0 }; + SMALL_RECT readRegion = { 0, 0, width - 1, height - 1 }; + trace("ReadConsoleOutputW: calling..."); + BOOL success = ReadConsoleOutputW(conout, buffer, bufSize, bufCoord, &readRegion); + trace("ReadConsoleOutputW: success=%d", success); + + assert(buffer[0].Char.UnicodeChar == L'A'); + assert(buffer[width * height - 2].Char.UnicodeChar == L'B'); + trace("Top-left and bottom-right characters read successfully!"); + + Sleep(30000); + + delete [] buffer; + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc b/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc new file mode 100644 index 0000000000..97bf59f998 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc @@ -0,0 +1,56 @@ +/* + * Sending VK_PAUSE to the console window almost works as a mechanism for + * pausing it, but it doesn't because the console could turn off the + * ENABLE_LINE_INPUT console mode flag. + */ + +#define _WIN32_WINNT 0x0501 +#include <stdio.h> +#include <stdlib.h> +#include <windows.h> + +CALLBACK DWORD pausingThread(LPVOID dummy) +{ + if (1) { + Sleep(1000); + HWND hwnd = GetConsoleWindow(); + SendMessage(hwnd, WM_KEYDOWN, VK_PAUSE, 1); + Sleep(1000); + SendMessage(hwnd, WM_KEYDOWN, VK_ESCAPE, 1); + } + + if (0) { + INPUT_RECORD ir; + memset(&ir, 0, sizeof(ir)); + ir.EventType = KEY_EVENT; + ir.Event.KeyEvent.bKeyDown = TRUE; + ir.Event.KeyEvent.wVirtualKeyCode = VK_PAUSE; + ir.Event.KeyEvent.wRepeatCount = 1; + } + + return 0; +} + +int main() +{ + HANDLE hin = GetStdHandle(STD_INPUT_HANDLE); + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + COORD c = { 0, 0 }; + + DWORD mode; + GetConsoleMode(hin, &mode); + SetConsoleMode(hin, mode & + ~(ENABLE_LINE_INPUT)); + + CreateThread(NULL, 0, + pausingThread, NULL, + 0, NULL); + + int i = 0; + while (true) { + Sleep(100); + printf("%d\n", ++i); + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc b/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc new file mode 100644 index 0000000000..82feaf3c50 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc @@ -0,0 +1,52 @@ +/* + * Demonstrates a conhost hang that occurs when widening the console buffer + * while selection is in progress. The problem affects the new Windows 10 + * console, not the "legacy" console mode that Windows 10 also includes. + * + * First tested with: + * - Windows 10.0.10240 + * - conhost.exe version 10.0.10240.16384 + * - ConhostV1.dll version 10.0.10240.16384 + * - ConhostV2.dll version 10.0.10240.16391 + */ + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <wchar.h> + +#include "TestUtil.cc" + +const int SC_CONSOLE_MARK = 0xFFF2; +const int SC_CONSOLE_SELECT_ALL = 0xFFF5; + +int main(int argc, char *argv[]) { + if (argc == 1) { + startChildProcess(L"CHILD"); + return 0; + } + + setWindowPos(0, 0, 1, 1); + setBufferSize(80, 25); + setWindowPos(0, 0, 80, 25); + + countDown(5); + + SendMessage(GetConsoleWindow(), WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0); + Sleep(2000); + + // This API call does not return. In the console window, the "Select All" + // operation appears to end. The console window becomes non-responsive, + // and the conhost.exe process must be killed from the Task Manager. + // (Killing this test program or closing the console window is not + // sufficient.) + // + // The same hang occurs whether line resizing is off or on. It happens + // with both "Mark" and "Select All". Calling setBufferSize with the + // existing buffer size does not hang, but calling it with only a changed + // buffer height *does* hang. Calling setWindowPos does not hang. + setBufferSize(120, 25); + + printf("Done...\n"); + Sleep(2000); +} diff --git a/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc b/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc new file mode 100644 index 0000000000..645fa95d54 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc @@ -0,0 +1,57 @@ +/* + * Demonstrates some wrapping behaviors of the new Windows 10 console. + */ + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + if (argc == 1) { + startChildProcess(L"CHILD"); + return 0; + } + + setWindowPos(0, 0, 1, 1); + setBufferSize(40, 20); + setWindowPos(0, 0, 40, 20); + + system("cls"); + + repeatChar(39, 'A'); repeatChar(1, ' '); + repeatChar(39, 'B'); repeatChar(1, ' '); + printf("\n"); + + repeatChar(39, 'C'); repeatChar(1, ' '); + repeatChar(39, 'D'); repeatChar(1, ' '); + printf("\n"); + + repeatChar(40, 'E'); + repeatChar(40, 'F'); + printf("\n"); + + repeatChar(39, 'G'); repeatChar(1, ' '); + repeatChar(39, 'H'); repeatChar(1, ' '); + printf("\n"); + + Sleep(2000); + + setChar(39, 0, '*', 0x24); + setChar(39, 1, '*', 0x24); + + setChar(39, 3, ' ', 0x24); + setChar(39, 4, ' ', 0x24); + + setChar(38, 6, ' ', 0x24); + setChar(38, 7, ' ', 0x24); + + Sleep(2000); + setWindowPos(0, 0, 35, 20); + setBufferSize(35, 20); + trace("DONE"); + + printf("Sleeping forever...\n"); + while(true) { Sleep(1000); } +} diff --git a/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc b/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc new file mode 100644 index 0000000000..50615fc8c7 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc @@ -0,0 +1,30 @@ +#include <windows.h> + +#include "TestUtil.cc" + +int main(int argc, char *argv[]) { + if (argc == 1) { + startChildProcess(L"CHILD"); + return 0; + } + + const int WIDTH = 25; + + setWindowPos(0, 0, 1, 1); + setBufferSize(WIDTH, 40); + setWindowPos(0, 0, WIDTH, 20); + + system("cls"); + + for (int i = 0; i < 100; ++i) { + printf("FOO(%d)\n", i); + } + + repeatChar(5, '\n'); + repeatChar(WIDTH * 5, '.'); + repeatChar(10, '\n'); + setWindowPos(0, 20, WIDTH, 20); + writeBox(0, 5, 1, 10, '|'); + + Sleep(120000); +} diff --git a/src/libs/3rdparty/winpty/misc/Win32Echo1.cc b/src/libs/3rdparty/winpty/misc/Win32Echo1.cc new file mode 100644 index 0000000000..06fc79f794 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win32Echo1.cc @@ -0,0 +1,26 @@ +/* + * A Win32 program that reads raw console input with ReadFile and echos + * it to stdout. + */ + +#include <stdio.h> +#include <conio.h> +#include <windows.h> + +int main() +{ + int count = 0; + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleMode(hStdIn, 0); + + while (true) { + DWORD actual; + char ch; + ReadFile(hStdIn, &ch, 1, &actual, NULL); + printf("%02x ", ch); + if (++count == 50) + break; + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Win32Echo2.cc b/src/libs/3rdparty/winpty/misc/Win32Echo2.cc new file mode 100644 index 0000000000..b2ea2ad1c5 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win32Echo2.cc @@ -0,0 +1,19 @@ +/* + * A Win32 program that reads raw console input with getch and echos + * it to stdout. + */ + +#include <stdio.h> +#include <conio.h> + +int main() +{ + int count = 0; + while (true) { + int ch = getch(); + printf("%02x ", ch); + if (++count == 50) + break; + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Win32Test1.cc b/src/libs/3rdparty/winpty/misc/Win32Test1.cc new file mode 100644 index 0000000000..a40d318a98 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win32Test1.cc @@ -0,0 +1,46 @@ +#define _WIN32_WINNT 0x0501 +#include "../src/shared/DebugClient.cc" +#include <windows.h> +#include <stdio.h> + +const int SC_CONSOLE_MARK = 0xFFF2; + +CALLBACK DWORD writerThread(void*) +{ + while (true) { + Sleep(1000); + trace("writing"); + printf("X\n"); + trace("written"); + } +} + +int main() +{ + CreateThread(NULL, 0, writerThread, NULL, 0, NULL); + trace("marking console"); + HWND hwnd = GetConsoleWindow(); + PostMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0); + + Sleep(2000); + + trace("reading output"); + CHAR_INFO buf[1]; + COORD bufSize = { 1, 1 }; + COORD zeroCoord = { 0, 0 }; + SMALL_RECT readRect = { 0, 0, 0, 0 }; + ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), + buf, + bufSize, + zeroCoord, + &readRect); + trace("done reading output"); + + Sleep(2000); + + PostMessage(hwnd, WM_CHAR, 27, 0x00010001); + + Sleep(1100); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Win32Test2.cc b/src/libs/3rdparty/winpty/misc/Win32Test2.cc new file mode 100644 index 0000000000..2777bad456 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win32Test2.cc @@ -0,0 +1,70 @@ +/* + * This test demonstrates that putting a console into selection mode does not + * block the low-level console APIs, even though it blocks WriteFile. + */ + +#define _WIN32_WINNT 0x0501 +#include "../src/shared/DebugClient.cc" +#include <windows.h> +#include <stdio.h> + +const int SC_CONSOLE_MARK = 0xFFF2; + +CALLBACK DWORD writerThread(void*) +{ + CHAR_INFO xChar, fillChar; + memset(&xChar, 0, sizeof(xChar)); + xChar.Char.AsciiChar = 'X'; + xChar.Attributes = 7; + memset(&fillChar, 0, sizeof(fillChar)); + fillChar.Char.AsciiChar = ' '; + fillChar.Attributes = 7; + COORD oneCoord = { 1, 1 }; + COORD zeroCoord = { 0, 0 }; + + while (true) { + SMALL_RECT writeRegion = { 5, 5, 5, 5 }; + WriteConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), + &xChar, oneCoord, + zeroCoord, + &writeRegion); + Sleep(500); + SMALL_RECT scrollRect = { 1, 1, 20, 20 }; + COORD destCoord = { 0, 0 }; + ScrollConsoleScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE), + &scrollRect, + NULL, + destCoord, + &fillChar); + } +} + +int main() +{ + CreateThread(NULL, 0, writerThread, NULL, 0, NULL); + trace("marking console"); + HWND hwnd = GetConsoleWindow(); + PostMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0); + + Sleep(2000); + + trace("reading output"); + CHAR_INFO buf[1]; + COORD bufSize = { 1, 1 }; + COORD zeroCoord = { 0, 0 }; + SMALL_RECT readRect = { 0, 0, 0, 0 }; + ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE), + buf, + bufSize, + zeroCoord, + &readRect); + trace("done reading output"); + + Sleep(2000); + + PostMessage(hwnd, WM_CHAR, 27, 0x00010001); + + Sleep(1100); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Win32Test3.cc b/src/libs/3rdparty/winpty/misc/Win32Test3.cc new file mode 100644 index 0000000000..1fb92aff3d --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win32Test3.cc @@ -0,0 +1,78 @@ +/* + * Creates a window station and starts a process under it. The new process + * also gets a new console. + */ + +#include <windows.h> +#include <string.h> +#include <stdio.h> + +int main() +{ + BOOL success; + + SECURITY_ATTRIBUTES sa; + memset(&sa, 0, sizeof(sa)); + sa.bInheritHandle = TRUE; + + HWINSTA originalStation = GetProcessWindowStation(); + printf("originalStation == 0x%x\n", originalStation); + HWINSTA station = CreateWindowStation(NULL, + 0, + WINSTA_ALL_ACCESS, + &sa); + printf("station == 0x%x\n", station); + if (!SetProcessWindowStation(station)) + printf("SetWindowStation failed!\n"); + HDESK desktop = CreateDesktop("Default", NULL, NULL, + /*dwFlags=*/0, GENERIC_ALL, + &sa); + printf("desktop = 0x%x\n", desktop); + + char stationName[256]; + stationName[0] = '\0'; + success = GetUserObjectInformation(station, UOI_NAME, + stationName, sizeof(stationName), + NULL); + printf("stationName = [%s]\n", stationName); + + char startupDesktop[256]; + sprintf(startupDesktop, "%s\\Default", stationName); + + STARTUPINFO sui; + PROCESS_INFORMATION pi; + memset(&sui, 0, sizeof(sui)); + memset(&pi, 0, sizeof(pi)); + sui.cb = sizeof(STARTUPINFO); + sui.lpDesktop = startupDesktop; + + // Start a cmd subprocess, and have it start its own cmd subprocess. + // Both subprocesses will connect to the same non-interactive window + // station. + + const char program[] = "c:\\windows\\system32\\cmd.exe"; + char cmdline[256]; + sprintf(cmdline, "%s /c cmd", program); + success = CreateProcess(program, + cmdline, + NULL, + NULL, + /*bInheritHandles=*/FALSE, + /*dwCreationFlags=*/CREATE_NEW_CONSOLE, + NULL, NULL, + &sui, + &pi); + + printf("pid == %d\n", pi.dwProcessId); + + // This sleep is necessary. We must give the child enough time to + // connect to the specified window station. + Sleep(5000); + + SetProcessWindowStation(originalStation); + CloseWindowStation(station); + CloseDesktop(desktop); + Sleep(5000); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/Win32Write1.cc b/src/libs/3rdparty/winpty/misc/Win32Write1.cc new file mode 100644 index 0000000000..6e5bf96682 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/Win32Write1.cc @@ -0,0 +1,44 @@ +/* + * A Win32 program that scrolls and writes to the console using the ioctl-like + * interface. + */ + +#include <stdio.h> +#include <windows.h> + +int main() +{ + HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + + for (int i = 0; i < 80; ++i) { + + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo(conout, &info); + + SMALL_RECT src = { 0, 1, info.dwSize.X - 1, info.dwSize.Y - 1 }; + COORD destOrigin = { 0, 0 }; + CHAR_INFO fillCharInfo = { 0 }; + fillCharInfo.Char.AsciiChar = ' '; + fillCharInfo.Attributes = 7; + ScrollConsoleScreenBuffer(conout, + &src, + NULL, + destOrigin, + &fillCharInfo); + + CHAR_INFO buffer = { 0 }; + buffer.Char.AsciiChar = 'X'; + buffer.Attributes = 7; + COORD bufferSize = { 1, 1 }; + COORD bufferCoord = { 0, 0 }; + SMALL_RECT writeRegion = { 0, 0, 0, 0 }; + writeRegion.Left = writeRegion.Right = i; + writeRegion.Top = writeRegion.Bottom = 5; + WriteConsoleOutput(conout, + &buffer, bufferSize, bufferCoord, + &writeRegion); + + Sleep(250); + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc b/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc new file mode 100644 index 0000000000..e6d9558df6 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc @@ -0,0 +1,27 @@ +// I noticed this on the ConEmu web site: +// +// https://social.msdn.microsoft.com/Forums/en-US/40c8e395-cca9-45c8-b9b8-2fbe6782ac2b/readconsoleoutput-cause-access-violation-writing-location-exception +// https://conemu.github.io/en/MicrosoftBugs.html +// +// In Windows 7, 8, and 8.1, a ReadConsoleOutputW with an out-of-bounds read +// region crashes the application. I have reproduced the problem on Windows 8 +// and 8.1, but not on Windows 7. +// + +#include <windows.h> + +#include "TestUtil.cc" + +int main() { + setWindowPos(0, 0, 1, 1); + setBufferSize(80, 25); + setWindowPos(0, 0, 80, 25); + + const HANDLE conout = openConout(); + static CHAR_INFO lineBuf[80]; + SMALL_RECT readRegion = { 0, 999, 79, 999 }; + const BOOL ret = ReadConsoleOutputW(conout, lineBuf, {80, 1}, {0, 0}, &readRegion); + ASSERT(!ret && "ReadConsoleOutputW should have failed"); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/WriteConsole.cc b/src/libs/3rdparty/winpty/misc/WriteConsole.cc new file mode 100644 index 0000000000..a03670ca92 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/WriteConsole.cc @@ -0,0 +1,106 @@ +#include <windows.h> + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +#include <string> +#include <vector> + +static std::wstring mbsToWcs(const std::string &s) { + const size_t len = mbstowcs(nullptr, s.c_str(), 0); + if (len == static_cast<size_t>(-1)) { + assert(false && "mbsToWcs: invalid string"); + } + std::wstring ret; + ret.resize(len); + const size_t len2 = mbstowcs(&ret[0], s.c_str(), len); + assert(len == len2); + return ret; +} + +uint32_t parseHex(wchar_t ch, bool &invalid) { + if (ch >= L'0' && ch <= L'9') { + return ch - L'0'; + } else if (ch >= L'a' && ch <= L'f') { + return ch - L'a' + 10; + } else if (ch >= L'A' && ch <= L'F') { + return ch - L'A' + 10; + } else { + invalid = true; + return 0; + } +} + +int main(int argc, char *argv[]) { + std::vector<std::wstring> args; + for (int i = 1; i < argc; ++i) { + args.push_back(mbsToWcs(argv[i])); + } + + std::wstring out; + for (const auto &arg : args) { + if (!out.empty()) { + out.push_back(L' '); + } + for (size_t i = 0; i < arg.size(); ++i) { + wchar_t ch = arg[i]; + wchar_t nch = i + 1 < arg.size() ? arg[i + 1] : L'\0'; + if (ch == L'\\') { + switch (nch) { + case L'a': ch = L'\a'; ++i; break; + case L'b': ch = L'\b'; ++i; break; + case L'e': ch = L'\x1b'; ++i; break; + case L'f': ch = L'\f'; ++i; break; + case L'n': ch = L'\n'; ++i; break; + case L'r': ch = L'\r'; ++i; break; + case L't': ch = L'\t'; ++i; break; + case L'v': ch = L'\v'; ++i; break; + case L'\\': ch = L'\\'; ++i; break; + case L'\'': ch = L'\''; ++i; break; + case L'\"': ch = L'\"'; ++i; break; + case L'\?': ch = L'\?'; ++i; break; + case L'x': + if (i + 3 < arg.size()) { + bool invalid = false; + uint32_t d1 = parseHex(arg[i + 2], invalid); + uint32_t d2 = parseHex(arg[i + 3], invalid); + if (!invalid) { + i += 3; + ch = (d1 << 4) | d2; + } + } + break; + case L'u': + if (i + 5 < arg.size()) { + bool invalid = false; + uint32_t d1 = parseHex(arg[i + 2], invalid); + uint32_t d2 = parseHex(arg[i + 3], invalid); + uint32_t d3 = parseHex(arg[i + 4], invalid); + uint32_t d4 = parseHex(arg[i + 5], invalid); + if (!invalid) { + i += 5; + ch = (d1 << 24) | (d2 << 16) | (d3 << 8) | d4; + } + } + break; + default: break; + } + } + out.push_back(ch); + } + } + + DWORD actual = 0; + if (!WriteConsoleW( + GetStdHandle(STD_OUTPUT_HANDLE), + out.c_str(), + out.size(), + &actual, + nullptr)) { + fprintf(stderr, "WriteConsole failed (is stdout a console?)\n"); + exit(1); + } + + return 0; +} diff --git a/src/libs/3rdparty/winpty/misc/build32.sh b/src/libs/3rdparty/winpty/misc/build32.sh new file mode 100644 index 0000000000..162993ce33 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/build32.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e +name=$1 +name=${name%.} +name=${name%.cc} +name=${name%.exe} +echo Compiling $name.cc to $name.exe +i686-w64-mingw32-g++.exe -static -std=c++11 $name.cc -o $name.exe +i686-w64-mingw32-strip $name.exe diff --git a/src/libs/3rdparty/winpty/misc/build64.sh b/src/libs/3rdparty/winpty/misc/build64.sh new file mode 100644 index 0000000000..6757967684 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/build64.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e +name=$1 +name=${name%.} +name=${name%.cc} +name=${name%.exe} +echo Compiling $name.cc to $name.exe +x86_64-w64-mingw32-g++.exe -static -std=c++11 $name.cc -o $name.exe +x86_64-w64-mingw32-strip $name.exe diff --git a/src/libs/3rdparty/winpty/misc/color-test.sh b/src/libs/3rdparty/winpty/misc/color-test.sh new file mode 100644 index 0000000000..065c8094e2 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/color-test.sh @@ -0,0 +1,212 @@ +#!/bin/bash + +FORE=$1 +BACK=$2 +FILL=$3 + +if [ "$FORE" = "" ]; then + FORE=DefaultFore +fi +if [ "$BACK" = "" ]; then + BACK=DefaultBack +fi + +# To detect color changes, we want a character that fills the whole cell +# if possible. U+2588 is perfect, except that it becomes invisible in the +# original xterm, when bolded. For that terminal, use something else, like +# "#" or "@". +if [ "$FILL" = "" ]; then + FILL="█" +fi + +# SGR (Select Graphic Rendition) +s() { + printf '\033[0m' + while [ "$1" != "" ]; do + printf '\033['"$1"'m' + shift + done +} + +# Print +p() { + echo -n "$@" +} + +# Print with newline +pn() { + echo "$@" +} + +# For practical reasons, sandwich black and white in-between the other colors. +FORE_COLORS="31 30 37 32 33 34 35 36" +BACK_COLORS="41 40 47 42 43 44 45 46" + + + +### Test order of Invert(7) -- it does not matter what order it appears in. + +# The Red color setting here (31) is shadowed by the green setting (32). The +# Reverse flag does not cause (32) to alter the background color immediately; +# instead, the Reverse flag is applied once to determine the final effective +# Fore/Back colors. +s 7 31 32; p " -- Should be: $BACK-on-green -- "; s; pn +s 31 7 32; p " -- Should be: $BACK-on-green -- "; s; pn +s 31 32 7; p " -- Should be: $BACK-on-green -- "; s; pn + +# As above, but for the background color. +s 7 41 42; p " -- Should be: green-on-$FORE -- "; s; pn +s 41 7 42; p " -- Should be: green-on-$FORE -- "; s; pn +s 41 42 7; p " -- Should be: green-on-$FORE -- "; s; pn + +# One last, related test +s 7; p "Invert text"; s 7 1; p " with some words bold"; s; pn; +s 0; p "Normal text"; s 0 1; p " with some words bold"; s; pn; + +pn + + + +### Test effect of Bold(1) on color, with and without Invert(7). + +# The Bold flag does not affect the background color when Reverse is missing. +# There should always be 8 colored boxes. +p " " +for x in $BACK_COLORS; do + s $x; p "-"; s $x 1; p "-" +done +s; pn " Bold should not affect background" + +# On some terminals, Bold affects color, and on some it doesn't. If there +# are only 8 colored boxes, then the next two tests will also show 8 colored +# boxes. If there are 16 boxes, then exactly one of the next two tests will +# also have 16 boxes. +p " " +for x in $FORE_COLORS; do + s $x; p "$FILL"; s $x 1; p "$FILL" +done +s; pn " Does bold affect foreground color?" + +# On some terminals, Bold+Invert highlights the final Background color. +p " " +for x in $FORE_COLORS; do + s $x 7; p "-"; s $x 7 1; p "-" +done +s; pn " Test if Bold+Invert affects background color" + +# On some terminals, Bold+Invert highlights the final Foreground color. +p " " +for x in $BACK_COLORS; do + s $x 7; p "$FILL"; s $x 7 1; p "$FILL" +done +s; pn " Test if Bold+Invert affects foreground color" + +pn + + + +### Test for support of ForeHi and BackHi properties. + +# ForeHi +p " " +for x in $FORE_COLORS; do + hi=$(( $x + 60 )) + s $x; p "$FILL"; s $hi; p "$FILL" +done +s; pn " Test for support of ForeHi colors" +p " " +for x in $FORE_COLORS; do + hi=$(( $x + 60 )) + s $x; p "$FILL"; s $x $hi; p "$FILL" +done +s; pn " Test for support of ForeHi colors (w/compat)" + +# BackHi +p " " +for x in $BACK_COLORS; do + hi=$(( $x + 60 )) + s $x; p "-"; s $hi; p "-" +done +s; pn " Test for support of BackHi colors" +p " " +for x in $BACK_COLORS; do + hi=$(( $x + 60 )) + s $x; p "-"; s $x $hi; p "-" +done +s; pn " Test for support of BackHi colors (w/compat)" + +pn + + + +### Identify the default fore and back colors. + +pn "Match default fore and back colors against 16-color palette" +pn " ==fore== ==back==" +for fore in $FORE_COLORS; do + forehi=$(( $fore + 60 )) + back=$(( $fore + 10 )) + backhi=$(( $back + 60 )) + p " " + s $fore; p "$FILL"; s; p "$FILL"; s $fore; p "$FILL"; s; p " " + s $forehi; p "$FILL"; s; p "$FILL"; s $forehi; p "$FILL"; s; p " " + s $back; p "-"; s; p "-"; s $back; p "-"; s; p " " + s $backhi; p "-"; s; p "-"; s $backhi; p "-"; s; p " " + pn " $fore $forehi $back $backhi" +done + +pn + + + +### Test coloring of rest-of-line. + +# +# When a new line is scrolled in, every cell in the line receives the +# current background color, which can be the default/transparent color. +# + +p "Newline with red background: usually no red -->"; s 41; pn +s; pn "This text is plain, but rest is red if scrolled -->" +s; p " "; s 41; printf '\033[1K'; s; printf '\033[1C'; pn "<-- red Erase-in-Line to beginning" +s; p "red Erase-in-Line to end -->"; s 41; printf '\033[0K'; s; pn +pn + + + +### Moving the cursor around does not change colors of anything. + +pn "Test modifying uncolored lines with a colored SGR:" +pn "aaaa" +pn +pn "____e" +s 31 42; printf '\033[4C\033[3A'; pn "bb" +pn "cccc" +pn "dddd" +s; pn + +pn "Test modifying colored+inverted+bold line with plain text:" +s 42 31 7 1; printf 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r'; +s; pn "This text is plain and followed by green-on-red -->" +pn + + + +### Full-width character overwriting + +pn 'Overwrite part of a full-width char with a half-width char' +p 'initial U+4000 ideographs -->'; s 31 42; p '䀀䀀'; s; pn +p 'write X to index #1 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[24G'; p X; s; pn +p 'write X to index #2 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[25G'; p X; s; pn +p 'write X to index #3 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[26G'; p X; s; pn +p 'write X to index #4 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[27G'; p X; s; pn +pn + +pn 'Verify that Erase-in-Line can "fix" last char in line' +p 'original -->'; s 31 42; p '䀀䀀'; s; pn +p 'overwrite -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[30G'; p 'XXX'; s; pn +p 'overwrite + Erase-in-Line -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[30G'; p 'XXX'; s; printf '\033[0K'; pn +p 'original -->'; s 31 42; p 'X䀀䀀'; s; pn +p 'overwrite -->'; s 31 42; p 'X䀀䀀'; s 35 44; printf '\033[30G'; p 'ーー'; s; pn +p 'overwrite + Erase-in-Line -->'; s 31 42; p 'X䀀䀀'; s 35 44; printf '\033[30G'; p 'ーー'; s; printf '\033[0K'; pn +pn diff --git a/src/libs/3rdparty/winpty/misc/font-notes.txt b/src/libs/3rdparty/winpty/misc/font-notes.txt new file mode 100644 index 0000000000..d4e36d8e25 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/font-notes.txt @@ -0,0 +1,300 @@ +================================================================== +Notes regarding fonts, code pages, and East Asian character widths +================================================================== + + +Registry settings +================= + + * There are console registry settings in `HKCU\Console`. That key has many + default settings (e.g. the default font settings) and also per-app subkeys + for app-specific overrides. + + * It is possible to override the code page with an app-specific setting. + + * There are registry settings in + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console`. In particular, + the `TrueTypeFont` subkey has a list of suitable font names associated with + various CJK code pages, as well as default font names. + + * There are two values in `HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage` + that specify the current code pages -- `OEMCP` and `ACP`. Setting the + system locale via the Control Panel's "Region" or "Language" dialogs seems + to change these code page values. + + +Console fonts +============= + + * The `FontFamily` field of `CONSOLE_FONT_INFOEX` has two parts: + - The high four bits can be exactly one of the `FF_xxxx` font families: + FF_DONTCARE(0x00) + FF_ROMAN(0x10) + FF_SWISS(0x20) + FF_MODERN(0x30) + FF_SCRIPT(0x40) + FF_DECORATIVE(0x50) + - The low four bits are a bitmask: + TMPF_FIXED_PITCH(1) -- actually means variable pitch + TMPF_VECTOR(2) + TMPF_TRUETYPE(4) + TMPF_DEVICE(8) + + * Each console has its own independent console font table. The current font + is identified with an index into this table. The size of the table is + returned by the undocumented `GetNumberOfConsoleFonts` API. It is apparently + possible to get the table size without this API, by instead calling + `GetConsoleFontSize` on each nonnegative index starting with 0 until the API + fails by returning (0, 0). + + * The font table grows dynamically. Each time the console is configured with + a previously-unused (FaceName, Size) combination, two entries are added to + the font table -- one with normal weight and one with bold weight. Fonts + added this way are always TrueType fonts. + + * Initially, the font table appears to contain only raster fonts. For + example, on an English Windows 8 installation, here is the initial font + table: + font 0: 4x6 + font 1: 6x8 + font 2: 8x8 + font 3: 16x8 + font 4: 5x12 + font 5: 7x12 + font 6: 8x12 -- the current font + font 7: 16x12 + font 8: 12x16 + font 9: 10x18 + `GetNumberOfConsoleFonts` returns 10, and this table matches the raster font + sizes according to the console properties dialog. + + * With a Japanese or Chinese locale, the initial font table appears to contain + the sizes applicable to both the East Asian raster font, as well as the + sizes for the CP437/CP1252 raster font. + + * The index passed to `SetCurrentConsoleFontEx` apparently has no effect. + The undocumented `SetConsoleFont` API, however, accepts *only* a font index, + and on Windows 8 English, it switches between all 10 fonts, even font index + #0. + + * If the index passed to `SetConsoleFont` identifies a Raster Font + incompatible with the current code page, then another Raster Font is + activated. + + * Passing "Terminal" to `SetCurrentConsoleFontEx` seems to have no effect. + Perhaps relatedly, `SetCurrentConsoleFontEx` does not fail if it is given a + bogus `FaceName`. Some font is still chosen and activated. Passing a face + name and height seems to work reliably, modulo the CP936 issue described + below. + + +Console fonts and code pages +============================ + + * On an English Windows installation, the default code page is 437, and it + cannot be set to 932 (Shift-JIS). (The API call fails.) Changing the + system locale to "Japanese (Japan)" using the Region/Language dialog + changes the default CP to 932 and permits changing the console CP between + 437 and 932. + + * A console has both an input code page and an output code page + (`{Get,Set}ConsoleCP` and `{Get,Set}ConsoleOutputCP`). I'm not going to + distinguish between the two for this document; presumably only the output + CP matters. The code page can change while the console is open, e.g. + by running `mode con: cp select={932,437,1252}` or by calling + `SetConsoleOutputCP`. + + * The current code page restricts which TrueType fonts and which Raster Font + sizes are available in the console properties dialog. This can change + while the console is open. + + * Changing the code page almost(?) always changes the current console font. + So far, I don't know how the new font is chosen. + + * With a CP of 932, the only TrueType font available in the console properties + dialog is "MS Gothic", displayed as "MS ゴシック". It is still possible to + use the English-default TrueType console fonts, Lucida Console and Consolas, + via `SetCurrentConsoleFontEx`. + + * When using a Raster Font and CP437 or CP1252, writing a UTF-16 codepoint not + representable in the code page instead writes a question mark ('?') to the + console. This conversion does not apply with a TrueType font, nor with the + Raster Font for CP932 or CP936. + + +ReadConsoleOutput and double-width characters +============================================== + + * With a Raster Font active, when `ReadConsoleOutputW` reads two cells of a + double-width character, it fills only a single `CHAR_INFO` structure. The + unused trailing `CHAR_INFO` structures are zero-filled. With a TrueType + font active, `ReadConsoleOutputW` instead fills two `CHAR_INFO` structures, + the first marked with `COMMON_LVB_LEADING_BYTE` and the second marked with + `COMMON_LVB_TRAILING_BYTE`. The flag is a misnomer--there aren't two + *bytes*, but two cells, and they have equal `CHAR_INFO.Char.UnicodeChar` + values. + + * `ReadConsoleOutputA`, on the other hand, reads two `CHAR_INFO` cells, and + if the UTF-16 value can be represented as two bytes in the ANSI/OEM CP, then + the two bytes are placed in the two `CHAR_INFO.Char.AsciiChar` values, and + the `COMMON_LVB_{LEADING,TRAILING}_BYTE` values are also used. If the + codepoint isn't representable, I don't remember what happens -- I think the + `AsciiChar` values take on an invalid marker. + + * Reading only one cell of a double-width character reads a space (U+0020) + instead. Raster-vs-TrueType and wide-vs-ANSI do not matter. + - XXX: what about attributes? Can a double-width character have mismatched + color attributes? + - XXX: what happens when writing to just one cell of a double-width + character? + + +Default Windows fonts for East Asian languages +============================================== +CP932 / Japanese: "MS ゴシック" (MS Gothic) +CP936 / Chinese Simplified: "新宋体" (SimSun) + + +Unreliable character width (half-width vs full-width) +===================================================== + +The half-width vs full-width status of a codepoint depends on at least these variables: + * OS version (Win10 legacy and new modes are different versions) + * system locale (English vs Japanese vs Chinese Simplified vs Chinese Traditional, etc) + * code page (437 vs 932 vs 936, etc) + * raster vs TrueType (Terminal vs MS Gothic vs SimSun, etc) + * font size + * rendered-vs-model (rendered width can be larger or smaller than model width) + +Example 1: U+2014 (EM DASH): East_Asian_Width: Ambiguous +-------------------------------------------------------- + rendered modeled +CP932: Win7/8 Raster Fonts half half +CP932: Win7/8 Gothic 14/15px half full +CP932: Win7/8 Consolas 14/15px half full +CP932: Win7/8 Lucida Console 14px half full +CP932: Win7/8 Lucida Console 15px half half +CP932: Win10New Raster Fonts half half +CP932: Win10New Gothic 14/15px half half +CP932: Win10New Consolas 14/15px half half +CP932: Win10New Lucida Console 14/15px half half + +CP936: Win7/8 Raster Fonts full full +CP936: Win7/8 SimSun 14px full full +CP936: Win7/8 SimSun 15px full half +CP936: Win7/8 Consolas 14/15px half full +CP936: Win10New Raster Fonts full full +CP936: Win10New SimSum 14/15px full full +CP936: Win10New Consolas 14/15px half half + +Example 2: U+3044 (HIRAGANA LETTER I): East_Asian_Width: Wide +------------------------------------------------------------- + rendered modeled +CP932: Win7/8/10N Raster Fonts full full +CP932: Win7/8/10N Gothic 14/15px full full +CP932: Win7/8/10N Consolas 14/15px half(*2) full +CP932: Win7/8/10N Lucida Console 14/15px half(*3) full + +CP936: Win7/8/10N Raster Fonts full full +CP936: Win7/8/10N SimSun 14/15px full full +CP936: Win7/8/10N Consolas 14/15px full full + +Example 3: U+30FC (KATAKANA-HIRAGANA PROLONGED SOUND MARK): East_Asian_Width: Wide +---------------------------------------------------------------------------------- + rendered modeled +CP932: Win7 Raster Fonts full full +CP932: Win7 Gothic 14/15px full full +CP932: Win7 Consolas 14/15px half(*2) full +CP932: Win7 Lucida Console 14px half(*3) full +CP932: Win7 Lucida Console 15px half(*3) half +CP932: Win8 Raster Fonts full full +CP932: Win8 Gothic 14px full half +CP932: Win8 Gothic 15px full full +CP932: Win8 Consolas 14/15px half(*2) full +CP932: Win8 Lucida Console 14px half(*3) full +CP932: Win8 Lucida Console 15px half(*3) half +CP932: Win10New Raster Fonts full full +CP932: Win10New Gothic 14/15px full full +CP932: Win10New Consolas 14/15px half(*2) half +CP932: Win10New Lucida Console 14/15px half(*2) half + +CP936: Win7/8 Raster Fonts full full +CP936: Win7/8 SimSun 14px full full +CP936: Win7/8 SimSun 15px full half +CP936: Win7/8 Consolas 14px full full +CP936: Win7/8 Consolas 15px full half +CP936: Win10New Raster Fonts full full +CP936: Win10New SimSum 14/15px full full +CP936: Win10New Consolas 14/15px full full + +Example 4: U+4000 (CJK UNIFIED IDEOGRAPH-4000): East_Asian_Width: Wide +---------------------------------------------------------------------- + rendered modeled +CP932: Win7 Raster Fonts half(*1) half +CP932: Win7 Gothic 14/15px full full +CP932: Win7 Consolas 14/15px half(*2) full +CP932: Win7 Lucida Console 14px half(*3) full +CP932: Win7 Lucida Console 15px half(*3) half +CP932: Win8 Raster Fonts half(*1) half +CP932: Win8 Gothic 14px full half +CP932: Win8 Gothic 15px full full +CP932: Win8 Consolas 14/15px half(*2) full +CP932: Win8 Lucida Console 14px half(*3) full +CP932: Win8 Lucida Console 15px half(*3) half +CP932: Win10New Raster Fonts half(*1) half +CP932: Win10New Gothic 14/15px full full +CP932: Win10New Consolas 14/15px half(*2) half +CP932: Win10New Lucida Console 14/15px half(*2) half + +CP936: Win7/8 Raster Fonts full full +CP936: Win7/8 SimSun 14px full full +CP936: Win7/8 SimSun 15px full half +CP936: Win7/8 Consolas 14px full full +CP936: Win7/8 Consolas 15px full half +CP936: Win10New Raster Fonts full full +CP936: Win10New SimSum 14/15px full full +CP936: Win10New Consolas 14/15px full full + +(*1) Rendered as a half-width filled white box +(*2) Rendered as a half-width box with a question mark inside +(*3) Rendered as a half-width empty box +(!!) One of the only places in Win10New where rendered and modeled width disagree + + +Windows quirk: unreliable font heights with CP936 / Chinese Simplified +====================================================================== + +When I set the font to 新宋体 17px, using either the properties dialog or +`SetCurrentConsoleFontEx`, the height reported by `GetCurrentConsoleFontEx` is +not 17, but is instead 19. The same problem does not affect Raster Fonts, +nor have I seen the problem in the English or Japanese locales. I observed +this with Windows 7 and Windows 10 new mode. + +If I set the font using the facename, width, *and* height, then the +`SetCurrentConsoleFontEx` and `GetCurrentConsoleFontEx` values agree. If I +set the font using *only* the facename and height, then the two values +disagree. + + +Windows bug: GetCurrentConsoleFontEx is initially invalid +========================================================= + + - Assume there is no configured console font name in the registry. In this + case, the console defaults to a raster font. + - Open a new console and call the `GetCurrentConsoleFontEx` API. + - The `FaceName` field of the returned `CONSOLE_FONT_INFOEX` data + structure is incorrect. On Windows 7, 8, and 10, I observed that the + field was blank. On Windows 8, occasionally, it instead contained: + U+AE72 U+75BE U+0001 + The other fields of the structure all appeared correct: + nFont=6 dwFontSize=(8,12) FontFamily=0x30 FontWeight=400 + - The `FaceName` field becomes initialized easily: + - Open the console properties dialog and click OK. (Cancel is not + sufficient.) + - Call the undocumented `SetConsoleFont` with the current font table + index, which is 6 in the example above. + - It seems that the console uncritically accepts whatever string is + stored in the registry, including a blank string, and passes it on the + the `GetCurrentConsoleFontEx` caller. It is possible to get the console + to *write* a blank setting into the registry -- simply open the console + (default or app-specific) properties and click OK. diff --git a/src/libs/3rdparty/winpty/misc/winbug-15048.cc b/src/libs/3rdparty/winpty/misc/winbug-15048.cc new file mode 100644 index 0000000000..0e98d648c5 --- /dev/null +++ b/src/libs/3rdparty/winpty/misc/winbug-15048.cc @@ -0,0 +1,201 @@ +/* + +Test program demonstrating a problem in Windows 15048's ReadConsoleOutput API. + +To compile: + + cl /nologo /EHsc winbug-15048.cc shell32.lib + +Example of regressed input: + +Case 1: + + > chcp 932 + > winbug-15048 -face-gothic 3044 + + Correct output: + + 1**34 (nb: U+3044 replaced with '**' to avoid MSVC encoding warning) + 5678 + + ReadConsoleOutputW (both rows, 3 cols) + row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007) + row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) + + ReadConsoleOutputW (both rows, 4 cols) + row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007) U+0034(0007) + row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) + + ReadConsoleOutputW (second row) + row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) + + ... + + Win10 15048 bad output: + + 1**34 + 5678 + + ReadConsoleOutputW (both rows, 3 cols) + row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0035(0007) + row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0000(0000) + + ReadConsoleOutputW (both rows, 4 cols) + row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0034(0007) U+0035(0007) + row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) U+0000(0000) + + ReadConsoleOutputW (second row) + row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) + + ... + + The U+3044 character (HIRAGANA LETTER I) occupies two columns, but it only + fills one record in the ReadConsoleOutput output buffer, which has the + effect of shifting the first cell of the second row into the last cell of + the first row. Ordinarily, the first and second cells would also have the + COMMON_LVB_LEADING_BYTE and COMMON_LVB_TRAILING_BYTE attributes set, which + allows winpty to detect the double-column character. + +Case 2: + + > chcp 437 + > winbug-15048 -face "Lucida Console" -h 4 221A + + The same issue happens with U+221A (SQUARE ROOT), but only in certain + fonts. The console seems to think this character occupies two columns + if the font is sufficiently small. The Windows console properties dialog + doesn't allow fonts below 5 pt, but winpty tries to use 2pt and 4pt Lucida + Console to allow very large console windows. + +Case 3: + + > chcp 437 + > winbug-15048 -face "Lucida Console" -h 12 FF12 + + The console selection system thinks U+FF12 (FULLWIDTH DIGIT TWO) occupies + two columns, which happens to be correct, but it's displayed as a single + column unrecognized character. It otherwise behaves the same as the other + cases. + +*/ + +#include <windows.h> +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <wchar.h> + +#include <string> + +#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0])) + +// See https://en.wikipedia.org/wiki/List_of_CJK_fonts +const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese +const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese +const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese +const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean + +static void set_font(const wchar_t *name, int size) { + const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_FONT_INFOEX fontex {}; + fontex.cbSize = sizeof(fontex); + fontex.dwFontSize.Y = size; + fontex.FontWeight = 400; + fontex.FontFamily = 0x36; + wcsncpy(fontex.FaceName, name, COUNT_OF(fontex.FaceName)); + assert(SetCurrentConsoleFontEx(conout, FALSE, &fontex)); +} + +static void usage(const wchar_t *prog) { + printf("Usage: %ls [options]\n", prog); + printf(" -h HEIGHT\n"); + printf(" -face FACENAME\n"); + printf(" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n"); + printf(" hhhh -- print U+hhhh\n"); + exit(1); +} + +static void dump_region(SMALL_RECT region, const char *name) { + const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + + CHAR_INFO buf[1000]; + memset(buf, 0xcc, sizeof(buf)); + + const int w = region.Right - region.Left + 1; + const int h = region.Bottom - region.Top + 1; + + assert(ReadConsoleOutputW( + conout, buf, { (short)w, (short)h }, { 0, 0 }, + ®ion)); + + printf("\n"); + printf("ReadConsoleOutputW (%s)\n", name); + for (int y = 0; y < h; ++y) { + printf("row %d: ", region.Top + y); + for (int i = 0; i < region.Left * 13; ++i) { + printf(" "); + } + for (int x = 0; x < w; ++x) { + const int i = y * w + x; + printf("U+%04x(%04x) ", buf[i].Char.UnicodeChar, buf[i].Attributes); + } + printf("\n"); + } +} + +int main() { + wchar_t *cmdline = GetCommandLineW(); + int argc = 0; + wchar_t **argv = CommandLineToArgvW(cmdline, &argc); + const wchar_t *font_name = L"Lucida Console"; + int font_height = 8; + int test_ch = 0xff12; // U+FF12 FULLWIDTH DIGIT TWO + + for (int i = 1; i < argc; ++i) { + const std::wstring arg = argv[i]; + const std::wstring next = i + 1 < argc ? argv[i + 1] : L""; + if (arg == L"-face" && i + 1 < argc) { + font_name = argv[i + 1]; + i++; + } else if (arg == L"-face-gothic") { + font_name = kMSGothic; + } else if (arg == L"-face-simsun") { + font_name = kNSimSun; + } else if (arg == L"-face-minglight") { + font_name = kMingLight; + } else if (arg == L"-face-gulimche") { + font_name = kGulimChe; + } else if (arg == L"-h" && i + 1 < argc) { + font_height = _wtoi(next.c_str()); + i++; + } else if (arg.c_str()[0] != '-') { + test_ch = wcstol(arg.c_str(), NULL, 16); + } else { + printf("Unrecognized argument: %ls\n", arg.c_str()); + usage(argv[0]); + } + } + + const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE); + + set_font(font_name, font_height); + + system("cls"); + DWORD actual = 0; + wchar_t output[] = L"1234\n5678\n"; + output[1] = test_ch; + WriteConsoleW(conout, output, 10, &actual, nullptr); + + dump_region({ 0, 0, 3, 1 }, "both rows, 3 cols"); + dump_region({ 0, 0, 4, 1 }, "both rows, 4 cols"); + dump_region({ 0, 1, 4, 1 }, "second row"); + dump_region({ 0, 0, 4, 0 }, "first row"); + dump_region({ 1, 0, 4, 0 }, "first row, skip 1"); + dump_region({ 2, 0, 4, 0 }, "first row, skip 2"); + dump_region({ 3, 0, 4, 0 }, "first row, skip 3"); + + set_font(font_name, 14); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat b/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat new file mode 100644 index 0000000000..b6bca7b079 --- /dev/null +++ b/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat @@ -0,0 +1,36 @@ +@echo off + +setlocal +cd %~dp0.. +set Path=C:\Python27;C:\Program Files\Git\cmd;%Path% + +call "%VS140COMNTOOLS%\VsDevCmd.bat" || goto :fail + +rmdir /s/q build-libpty 2>NUL +mkdir build-libpty\win +mkdir build-libpty\win\x86 +mkdir build-libpty\win\x86_64 +mkdir build-libpty\win\xp + +rmdir /s/q src\Release 2>NUL +rmdir /s/q src\.vs 2>NUL +del src\*.vcxproj src\*.vcxproj.filters src\*.sln src\*.sdf 2>NUL + +call vcbuild.bat --msvc-platform Win32 --gyp-msvs-version 2015 --toolset v140_xp || goto :fail +copy src\Release\Win32\winpty.dll build-libpty\win\xp || goto :fail +copy src\Release\Win32\winpty-agent.exe build-libpty\win\xp || goto :fail + +call vcbuild.bat --msvc-platform Win32 --gyp-msvs-version 2015 || goto :fail +copy src\Release\Win32\winpty.dll build-libpty\win\x86 || goto :fail +copy src\Release\Win32\winpty-agent.exe build-libpty\win\x86 || goto :fail + +call vcbuild.bat --msvc-platform x64 --gyp-msvs-version 2015 || goto :fail +copy src\Release\x64\winpty.dll build-libpty\win\x86_64 || goto :fail +copy src\Release\x64\winpty-agent.exe build-libpty\win\x86_64 || goto :fail + +echo success +goto :EOF + +:fail +echo error: build failed +exit /b 1 diff --git a/src/libs/3rdparty/winpty/ship/common_ship.py b/src/libs/3rdparty/winpty/ship/common_ship.py new file mode 100644 index 0000000000..b587ac7ce1 --- /dev/null +++ b/src/libs/3rdparty/winpty/ship/common_ship.py @@ -0,0 +1,89 @@ +import os +import sys + +# These scripts need to continue using Python 2 rather than 3, because +# make_msvc_package.py puts the current Python interpreter on the PATH for the +# sake of gyp, and gyp doesn't work with Python 3 yet. +# https://bugs.chromium.org/p/gyp/issues/detail?id=36 +if os.name != "nt": + sys.exit("Error: ship scripts require native Python 2.7. (wrong os.name)") +if sys.version_info[0:2] != (2,7): + sys.exit("Error: ship scripts require native Python 2.7. (wrong version)") + +import glob +import hashlib +import shutil +import subprocess +from distutils.spawn import find_executable + +topDir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + +with open(topDir + "/VERSION.txt", "rt") as f: + winptyVersion = f.read().strip() + +def rmrf(patterns): + for pattern in patterns: + for path in glob.glob(pattern): + if os.path.isdir(path) and not os.path.islink(path): + print("+ rm -r " + path) + sys.stdout.flush() + shutil.rmtree(path) + elif os.path.isfile(path): + print("+ rm " + path) + sys.stdout.flush() + os.remove(path) + +def mkdir(path): + if not os.path.isdir(path): + os.makedirs(path) + +def requireExe(name, guesses): + if find_executable(name) is None: + for guess in guesses: + if os.path.exists(guess): + newDir = os.path.dirname(guess) + print("Adding " + newDir + " to Path to provide " + name) + os.environ["Path"] = newDir + ";" + os.environ["Path"] + ret = find_executable(name) + if ret is None: + sys.exit("Error: required EXE is missing from Path: " + name) + return ret + +class ModifyEnv: + def __init__(self, **kwargs): + self._changes = dict(kwargs) + self._original = dict() + + def __enter__(self): + for var, val in self._changes.items(): + self._original[var] = os.environ[var] + os.environ[var] = val + + def __exit__(self, type, value, traceback): + for var, val in self._original.items(): + os.environ[var] = val + +def sha256(path): + with open(path, "rb") as fp: + return hashlib.sha256(fp.read()).hexdigest() + +def checkSha256(path, expected): + actual = sha256(path) + if actual != expected: + sys.exit("error: sha256 hash mismatch on {}: expected {}, found {}".format( + path, expected, actual)) + +requireExe("git.exe", [ + "C:\\Program Files\\Git\\cmd\\git.exe", + "C:\\Program Files (x86)\\Git\\cmd\\git.exe" +]) + +commitHash = subprocess.check_output(["git.exe", "rev-parse", "HEAD"]).strip() +defaultPathEnviron = "C:\\Windows\\System32;C:\\Windows" + +ZIP_TOOL = requireExe("7z.exe", [ + "C:\\Program Files\\7-Zip\\7z.exe", + "C:\\Program Files (x86)\\7-Zip\\7z.exe", +]) + +requireExe("curl.exe", []) diff --git a/src/libs/3rdparty/winpty/ship/make_msvc_package.py b/src/libs/3rdparty/winpty/ship/make_msvc_package.py new file mode 100644 index 0000000000..2d10aac787 --- /dev/null +++ b/src/libs/3rdparty/winpty/ship/make_msvc_package.py @@ -0,0 +1,163 @@ +#!python + +# Copyright (c) 2016 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. + +# +# Run with native CPython 2.7. +# +# This script looks for MSVC using a version-specific environment variable, +# such as VS140COMNTOOLS for MSVC 2015. +# + +import common_ship + +import argparse +import os +import shutil +import subprocess +import sys + +os.chdir(common_ship.topDir) + +MSVC_VERSION_TABLE = { + "2015" : { + "package_name" : "msvc2015", + "gyp_version" : "2015", + "common_tools_env" : "VS140COMNTOOLS", + "xp_toolset" : "v140_xp", + }, + "2013" : { + "package_name" : "msvc2013", + "gyp_version" : "2013", + "common_tools_env" : "VS120COMNTOOLS", + "xp_toolset" : "v120_xp", + }, +} + +ARCH_TABLE = { + "x64" : { + "msvc_platform" : "x64", + }, + "ia32" : { + "msvc_platform" : "Win32", + }, +} + +def readArguments(): + parser = argparse.ArgumentParser() + parser.add_argument("--msvc-version", default="2015") + ret = parser.parse_args() + if ret.msvc_version not in MSVC_VERSION_TABLE: + sys.exit("Error: unrecognized version: " + ret.msvc_version + ". " + + "Versions: " + " ".join(sorted(MSVC_VERSION_TABLE.keys()))) + return ret + +ARGS = readArguments() + +def checkoutGyp(): + if os.path.isdir("build-gyp"): + return + subprocess.check_call([ + "git.exe", + "clone", + "https://chromium.googlesource.com/external/gyp", + "build-gyp" + ]) + +def cleanMsvc(): + common_ship.rmrf(""" + src/Release src/.vs src/gen + src/*.vcxproj src/*.vcxproj.filters src/*.sln src/*.sdf + """.split()) + +def build(arch, packageDir, xp=False): + archInfo = ARCH_TABLE[arch] + versionInfo = MSVC_VERSION_TABLE[ARGS.msvc_version] + + devCmdPath = os.path.join(os.environ[versionInfo["common_tools_env"]], "VsDevCmd.bat") + if not os.path.isfile(devCmdPath): + sys.exit("Error: MSVC environment script missing: " + devCmdPath) + + toolsetArgument = " --toolset {}".format(versionInfo["xp_toolset"]) if xp else "" + newEnv = os.environ.copy() + newEnv["PATH"] = os.path.dirname(sys.executable) + ";" + common_ship.defaultPathEnviron + commandLine = ( + '"' + devCmdPath + '" && ' + + " vcbuild.bat" + + " --gyp-msvs-version " + versionInfo["gyp_version"] + + " --msvc-platform " + archInfo["msvc_platform"] + + " --commit-hash " + common_ship.commitHash + + toolsetArgument + ) + + subprocess.check_call(commandLine, shell=True, env=newEnv) + + archPackageDir = os.path.join(packageDir, arch) + if xp: + archPackageDir += "_xp" + + common_ship.mkdir(archPackageDir + "/bin") + common_ship.mkdir(archPackageDir + "/lib") + + binSrc = os.path.join(common_ship.topDir, "src/Release", archInfo["msvc_platform"]) + + shutil.copy(binSrc + "/winpty.dll", archPackageDir + "/bin") + shutil.copy(binSrc + "/winpty-agent.exe", archPackageDir + "/bin") + shutil.copy(binSrc + "/winpty-debugserver.exe", archPackageDir + "/bin") + shutil.copy(binSrc + "/winpty.lib", archPackageDir + "/lib") + +def buildPackage(): + versionInfo = MSVC_VERSION_TABLE[ARGS.msvc_version] + + packageName = "winpty-%s-%s" % ( + common_ship.winptyVersion, + versionInfo["package_name"], + ) + + packageRoot = os.path.join(common_ship.topDir, "ship/packages") + packageDir = os.path.join(packageRoot, packageName) + packageFile = packageDir + ".zip" + + common_ship.rmrf([packageDir]) + common_ship.rmrf([packageFile]) + common_ship.mkdir(packageDir) + + checkoutGyp() + cleanMsvc() + build("ia32", packageDir, True) + build("x64", packageDir, True) + cleanMsvc() + build("ia32", packageDir) + build("x64", packageDir) + + topDir = common_ship.topDir + + common_ship.mkdir(packageDir + "/include") + shutil.copy(topDir + "/src/include/winpty.h", packageDir + "/include") + shutil.copy(topDir + "/src/include/winpty_constants.h", packageDir + "/include") + shutil.copy(topDir + "/LICENSE", packageDir) + shutil.copy(topDir + "/README.md", packageDir) + shutil.copy(topDir + "/RELEASES.md", packageDir) + + subprocess.check_call([common_ship.ZIP_TOOL, "a", packageFile, "."], cwd=packageDir) + +if __name__ == "__main__": + buildPackage() diff --git a/src/libs/3rdparty/winpty/ship/ship.py b/src/libs/3rdparty/winpty/ship/ship.py new file mode 100644 index 0000000000..44f5862e3e --- /dev/null +++ b/src/libs/3rdparty/winpty/ship/ship.py @@ -0,0 +1,104 @@ +#!python + +# 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. + +# +# Run with native CPython 2.7 on a 64-bit computer. +# + +import common_ship + +from optparse import OptionParser +import multiprocessing +import os +import shutil +import subprocess +import sys + + +def dllVersion(path): + version = subprocess.check_output( + ["powershell.exe", + "[System.Diagnostics.FileVersionInfo]::GetVersionInfo(\"" + path + "\").FileVersion"]) + return version.strip() + + +os.chdir(common_ship.topDir) + + +# Determine other build parameters. +BUILD_KINDS = { + "cygwin": { + "path": ["bin"], + "dll": "bin\\cygwin1.dll", + }, + "msys2": { + "path": ["opt\\bin", "usr\\bin"], + "dll": "usr\\bin\\msys-2.0.dll", + }, +} + + +def buildTarget(kind, syspath, arch): + + binPaths = [os.path.join(syspath, p) for p in BUILD_KINDS[kind]["path"]] + binPaths += common_ship.defaultPathEnviron.split(";") + newPath = ";".join(binPaths) + + dllver = dllVersion(os.path.join(syspath, BUILD_KINDS[kind]["dll"])) + packageName = "winpty-{}-{}-{}-{}".format(common_ship.winptyVersion, kind, dllver, arch) + if os.path.exists("ship\\packages\\" + packageName): + shutil.rmtree("ship\\packages\\" + packageName) + + print("+ Setting PATH to: {}".format(newPath)) + with common_ship.ModifyEnv(PATH=newPath): + subprocess.check_call(["sh.exe", "configure"]) + subprocess.check_call(["make.exe", "clean"]) + makeBaseCmd = [ + "make.exe", + "COMMIT_HASH=" + common_ship.commitHash, + "PREFIX=ship/packages/" + packageName + ] + subprocess.check_call(makeBaseCmd + ["all", "tests", "-j%d" % multiprocessing.cpu_count()]) + subprocess.check_call(["build\\trivial_test.exe"]) + subprocess.check_call(makeBaseCmd + ["install"]) + subprocess.check_call(["tar.exe", "cvfz", + packageName + ".tar.gz", + packageName], cwd=os.path.join(os.getcwd(), "ship", "packages")) + + +def main(): + parser = OptionParser() + parser.add_option("--kind", type="choice", choices=["cygwin", "msys2"]) + parser.add_option("--syspath") + parser.add_option("--arch", type="choice", choices=["ia32", "x64"]) + (args, extra) = parser.parse_args() + + args.kind or parser.error("--kind must be specified") + args.arch or parser.error("--arch must be specified") + args.syspath or parser.error("--syspath must be specified") + extra and parser.error("unexpected positional argument(s)") + + buildTarget(args.kind, args.syspath, args.arch) + + +if __name__ == "__main__": + main() diff --git a/src/libs/3rdparty/winpty/src/CMakeLists.txt b/src/libs/3rdparty/winpty/src/CMakeLists.txt new file mode 100644 index 0000000000..22b15111d4 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/CMakeLists.txt @@ -0,0 +1,111 @@ +if (MSVC) + add_compile_definitions(NOMINMAX UNICODE _UNICODE) +endif() + +file(WRITE ${CMAKE_BINARY_DIR}/GenVersion.h.in [=[ +const char GenVersion_Version[] = "@VERSION@"; +const char GenVersion_Commit[] = "@COMMIT_HASH@"; +]=]) + +file(READ ../VERSION.txt VERSION) +string(REPLACE "\n" "" VERSION "${VERSION}") +configure_file(${CMAKE_BINARY_DIR}/GenVersion.h.in ${CMAKE_BINARY_DIR}/GenVersion.h @ONLY) + +set(shared_sources + shared/AgentMsg.h + shared/BackgroundDesktop.h + shared/BackgroundDesktop.cc + shared/Buffer.h + shared/Buffer.cc + shared/DebugClient.h + shared/DebugClient.cc + shared/GenRandom.h + shared/GenRandom.cc + shared/OsModule.h + shared/OwnedHandle.h + shared/OwnedHandle.cc + shared/StringBuilder.h + shared/StringUtil.cc + shared/StringUtil.h + shared/UnixCtrlChars.h + shared/WindowsSecurity.cc + shared/WindowsSecurity.h + shared/WindowsVersion.h + shared/WindowsVersion.cc + shared/WinptyAssert.h + shared/WinptyAssert.cc + shared/WinptyException.h + shared/WinptyException.cc + shared/WinptyVersion.h + shared/WinptyVersion.cc + shared/winpty_snprintf.h +) + +# +# winpty-agent +# + +add_qtc_executable(winpty-agent + INCLUDES + include ${CMAKE_BINARY_DIR} + DEFINES WINPTY_AGENT_ASSERT + PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON + SOURCES + agent/Agent.h + agent/Agent.cc + agent/AgentCreateDesktop.h + agent/AgentCreateDesktop.cc + agent/ConsoleFont.cc + agent/ConsoleFont.h + agent/ConsoleInput.cc + agent/ConsoleInput.h + agent/ConsoleInputReencoding.cc + agent/ConsoleInputReencoding.h + agent/ConsoleLine.cc + agent/ConsoleLine.h + agent/Coord.h + agent/DebugShowInput.h + agent/DebugShowInput.cc + agent/DefaultInputMap.h + agent/DefaultInputMap.cc + agent/DsrSender.h + agent/EventLoop.h + agent/EventLoop.cc + agent/InputMap.h + agent/InputMap.cc + agent/LargeConsoleRead.h + agent/LargeConsoleRead.cc + agent/NamedPipe.h + agent/NamedPipe.cc + agent/Scraper.h + agent/Scraper.cc + agent/SimplePool.h + agent/SmallRect.h + agent/Terminal.h + agent/Terminal.cc + agent/UnicodeEncoding.h + agent/Win32Console.cc + agent/Win32Console.h + agent/Win32ConsoleBuffer.cc + agent/Win32ConsoleBuffer.h + agent/main.cc + ${shared_sources} +) + +# +# libwinpty +# + +add_qtc_library(winpty STATIC + INCLUDES ${CMAKE_BINARY_DIR} + PUBLIC_DEFINES COMPILING_WINPTY_DLL + PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON + SOURCES + libwinpty/AgentLocation.cc + libwinpty/AgentLocation.h + libwinpty/winpty.cc + ${shared_sources} +) + +target_include_directories(winpty + PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) diff --git a/src/libs/3rdparty/winpty/src/agent/Agent.cc b/src/libs/3rdparty/winpty/src/agent/Agent.cc new file mode 100644 index 0000000000..986edead13 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Agent.cc @@ -0,0 +1,612 @@ +// Copyright (c) 2011-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 "Agent.h" + +#include <windows.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <algorithm> +#include <string> +#include <utility> +#include <vector> + +#include "../include/winpty_constants.h" + +#include "../shared/AgentMsg.h" +#include "../shared/Buffer.h" +#include "../shared/DebugClient.h" +#include "../shared/GenRandom.h" +#include "../shared/StringBuilder.h" +#include "../shared/StringUtil.h" +#include "../shared/WindowsVersion.h" +#include "../shared/WinptyAssert.h" + +#include "ConsoleFont.h" +#include "ConsoleInput.h" +#include "NamedPipe.h" +#include "Scraper.h" +#include "Terminal.h" +#include "Win32ConsoleBuffer.h" + +namespace { + +static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType) +{ + if (dwCtrlType == CTRL_C_EVENT) { + // Do nothing and claim to have handled the event. + return TRUE; + } + return FALSE; +} + +// We can detect the new Windows 10 console by observing the effect of the +// Mark command. In older consoles, Mark temporarily moves the cursor to the +// top-left of the console window. In the new console, the cursor isn't +// initially moved. +// +// We might like to use Mark to freeze the console, but we can't, because when +// the Mark command ends, the console moves the cursor back to its starting +// point, even if the console application has moved it in the meantime. +static void detectNewWindows10Console( + Win32Console &console, Win32ConsoleBuffer &buffer) +{ + if (!isAtLeastWindows8()) { + return; + } + + ConsoleScreenBufferInfo info = buffer.bufferInfo(); + + // Make sure the window isn't 1x1. AFAIK, this should never happen + // accidentally. It is difficult to make it happen deliberately. + if (info.srWindow.Left == info.srWindow.Right && + info.srWindow.Top == info.srWindow.Bottom) { + trace("detectNewWindows10Console: Initial console window was 1x1 -- " + "expanding for test"); + setSmallFont(buffer.conout(), 400, false); + buffer.moveWindow(SmallRect(0, 0, 1, 1)); + buffer.resizeBuffer(Coord(400, 1)); + buffer.moveWindow(SmallRect(0, 0, 2, 1)); + // This use of GetLargestConsoleWindowSize ought to be unnecessary + // given the behavior I've seen from moveWindow(0, 0, 1, 1), but + // I'd like to be especially sure, considering that this code will + // rarely be tested. + const auto largest = GetLargestConsoleWindowSize(buffer.conout()); + buffer.moveWindow( + SmallRect(0, 0, std::min(largest.X, buffer.bufferSize().X), 1)); + info = buffer.bufferInfo(); + ASSERT(info.srWindow.Right > info.srWindow.Left && + "Could not expand console window from 1x1"); + } + + // Test whether MARK moves the cursor. + const Coord initialPosition(info.srWindow.Right, info.srWindow.Bottom); + buffer.setCursorPosition(initialPosition); + ASSERT(!console.frozen()); + console.setFreezeUsesMark(true); + console.setFrozen(true); + const bool isNewW10 = (buffer.cursorPosition() == initialPosition); + console.setFrozen(false); + buffer.setCursorPosition(Coord(0, 0)); + + trace("Attempting to detect new Windows 10 console using MARK: %s", + isNewW10 ? "detected" : "not detected"); + console.setFreezeUsesMark(false); + console.setNewW10(isNewW10); +} + +static inline WriteBuffer newPacket() { + WriteBuffer packet; + packet.putRawValue<uint64_t>(0); // Reserve space for size. + return packet; +} + +static HANDLE duplicateHandle(HANDLE h) { + HANDLE ret = nullptr; + if (!DuplicateHandle( + GetCurrentProcess(), h, + GetCurrentProcess(), &ret, + 0, FALSE, DUPLICATE_SAME_ACCESS)) { + ASSERT(false && "DuplicateHandle failed!"); + } + return ret; +} + +// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it +// back to 64-bits. See the MSDN article, "Interprocess Communication Between +// 32-bit and 64-bit Applications". +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx +static int64_t int64FromHandle(HANDLE h) { + return static_cast<int64_t>(reinterpret_cast<intptr_t>(h)); +} + +} // anonymous namespace + +Agent::Agent(LPCWSTR controlPipeName, + uint64_t agentFlags, + int mouseMode, + int initialCols, + int initialRows) : + m_useConerr((agentFlags & WINPTY_FLAG_CONERR) != 0), + m_plainMode((agentFlags & WINPTY_FLAG_PLAIN_OUTPUT) != 0), + m_mouseMode(mouseMode) +{ + trace("Agent::Agent entered"); + + ASSERT(initialCols >= 1 && initialRows >= 1); + initialCols = std::min(initialCols, MAX_CONSOLE_WIDTH); + initialRows = std::min(initialRows, MAX_CONSOLE_HEIGHT); + + const bool outputColor = + !m_plainMode || (agentFlags & WINPTY_FLAG_COLOR_ESCAPES); + const Coord initialSize(initialCols, initialRows); + + auto primaryBuffer = openPrimaryBuffer(); + if (m_useConerr) { + m_errorBuffer = Win32ConsoleBuffer::createErrorBuffer(); + } + + detectNewWindows10Console(m_console, *primaryBuffer); + + m_controlPipe = &connectToControlPipe(controlPipeName); + m_coninPipe = &createDataServerPipe(false, L"conin"); + m_conoutPipe = &createDataServerPipe(true, L"conout"); + if (m_useConerr) { + m_conerrPipe = &createDataServerPipe(true, L"conerr"); + } + + // Send an initial response packet to winpty.dll containing pipe names. + { + auto setupPacket = newPacket(); + setupPacket.putWString(m_coninPipe->name()); + setupPacket.putWString(m_conoutPipe->name()); + if (m_useConerr) { + setupPacket.putWString(m_conerrPipe->name()); + } + writePacket(setupPacket); + } + + std::unique_ptr<Terminal> primaryTerminal; + primaryTerminal.reset(new Terminal(*m_conoutPipe, + m_plainMode, + outputColor)); + m_primaryScraper.reset(new Scraper(m_console, + *primaryBuffer, + std::move(primaryTerminal), + initialSize)); + if (m_useConerr) { + std::unique_ptr<Terminal> errorTerminal; + errorTerminal.reset(new Terminal(*m_conerrPipe, + m_plainMode, + outputColor)); + m_errorScraper.reset(new Scraper(m_console, + *m_errorBuffer, + std::move(errorTerminal), + initialSize)); + } + + m_console.setTitle(m_currentTitle); + + const HANDLE conin = GetStdHandle(STD_INPUT_HANDLE); + m_consoleInput.reset( + new ConsoleInput(conin, m_mouseMode, *this, m_console)); + + // Setup Ctrl-C handling. First restore default handling of Ctrl-C. This + // attribute is inherited by child processes. Then register a custom + // Ctrl-C handler that does nothing. The handler will be called when the + // agent calls GenerateConsoleCtrlEvent. + SetConsoleCtrlHandler(NULL, FALSE); + SetConsoleCtrlHandler(consoleCtrlHandler, TRUE); + + setPollInterval(25); +} + +Agent::~Agent() +{ + trace("Agent::~Agent entered"); + agentShutdown(); + if (m_childProcess != NULL) { + CloseHandle(m_childProcess); + } +} + +// Write a "Device Status Report" command to the terminal. The terminal will +// reply with a row+col escape sequence. Presumably, the DSR reply will not +// split a keypress escape sequence, so it should be safe to assume that the +// bytes before it are complete keypresses. +void Agent::sendDsr() +{ + if (!m_plainMode && !m_conoutPipe->isClosed()) { + m_conoutPipe->write("\x1B[6n"); + } +} + +NamedPipe &Agent::connectToControlPipe(LPCWSTR pipeName) +{ + NamedPipe &pipe = createNamedPipe(); + pipe.connectToServer(pipeName, NamedPipe::OpenMode::Duplex); + pipe.setReadBufferSize(64 * 1024); + return pipe; +} + +// Returns a new server named pipe. It has not yet been connected. +NamedPipe &Agent::createDataServerPipe(bool write, const wchar_t *kind) +{ + const auto name = + (WStringBuilder(128) + << L"\\\\.\\pipe\\winpty-" + << kind << L'-' + << GenRandom().uniqueName()).str_moved(); + NamedPipe &pipe = createNamedPipe(); + pipe.openServerPipe( + name.c_str(), + write ? NamedPipe::OpenMode::Writing + : NamedPipe::OpenMode::Reading, + write ? 8192 : 0, + write ? 0 : 256); + if (!write) { + pipe.setReadBufferSize(64 * 1024); + } + return pipe; +} + +void Agent::onPipeIo(NamedPipe &namedPipe) +{ + if (&namedPipe == m_conoutPipe || &namedPipe == m_conerrPipe) { + autoClosePipesForShutdown(); + } else if (&namedPipe == m_coninPipe) { + pollConinPipe(); + } else if (&namedPipe == m_controlPipe) { + pollControlPipe(); + } +} + +void Agent::pollControlPipe() +{ + if (m_controlPipe->isClosed()) { + trace("Agent exiting (control pipe is closed)"); + shutdown(); + return; + } + + while (true) { + uint64_t packetSize = 0; + const auto amt1 = + m_controlPipe->peek(&packetSize, sizeof(packetSize)); + if (amt1 < sizeof(packetSize)) { + break; + } + ASSERT(packetSize >= sizeof(packetSize) && packetSize <= SIZE_MAX); + if (m_controlPipe->bytesAvailable() < packetSize) { + if (m_controlPipe->readBufferSize() < packetSize) { + m_controlPipe->setReadBufferSize(packetSize); + } + break; + } + std::vector<char> packetData; + packetData.resize(packetSize); + const auto amt2 = m_controlPipe->read(packetData.data(), packetSize); + ASSERT(amt2 == packetSize); + try { + ReadBuffer buffer(std::move(packetData)); + buffer.getRawValue<uint64_t>(); // Discard the size. + handlePacket(buffer); + } catch (const ReadBuffer::DecodeError&) { + ASSERT(false && "Decode error"); + } + } +} + +void Agent::handlePacket(ReadBuffer &packet) +{ + const int type = packet.getInt32(); + switch (type) { + case AgentMsg::StartProcess: + handleStartProcessPacket(packet); + break; + case AgentMsg::SetSize: + // TODO: I think it might make sense to collapse consecutive SetSize + // messages. i.e. The terminal process can probably generate SetSize + // messages faster than they can be processed, and some GUIs might + // generate a flood of them, so if we can read multiple SetSize packets + // at once, we can ignore the early ones. + handleSetSizePacket(packet); + break; + case AgentMsg::GetConsoleProcessList: + handleGetConsoleProcessListPacket(packet); + break; + default: + trace("Unrecognized message, id:%d", type); + } +} + +void Agent::writePacket(WriteBuffer &packet) +{ + const auto &bytes = packet.buf(); + packet.replaceRawValue<uint64_t>(0, bytes.size()); + m_controlPipe->write(bytes.data(), bytes.size()); +} + +void Agent::handleStartProcessPacket(ReadBuffer &packet) +{ + ASSERT(m_childProcess == nullptr); + ASSERT(!m_closingOutputPipes); + + const uint64_t spawnFlags = packet.getInt64(); + const bool wantProcessHandle = packet.getInt32() != 0; + const bool wantThreadHandle = packet.getInt32() != 0; + const auto program = packet.getWString(); + const auto cmdline = packet.getWString(); + const auto cwd = packet.getWString(); + const auto env = packet.getWString(); + const auto desktop = packet.getWString(); + packet.assertEof(); + + auto cmdlineV = vectorWithNulFromString(cmdline); + auto desktopV = vectorWithNulFromString(desktop); + auto envV = vectorFromString(env); + + LPCWSTR programArg = program.empty() ? nullptr : program.c_str(); + LPWSTR cmdlineArg = cmdline.empty() ? nullptr : cmdlineV.data(); + LPCWSTR cwdArg = cwd.empty() ? nullptr : cwd.c_str(); + LPWSTR envArg = env.empty() ? nullptr : envV.data(); + + STARTUPINFOW sui = {}; + PROCESS_INFORMATION pi = {}; + sui.cb = sizeof(sui); + sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data(); + BOOL inheritHandles = FALSE; + if (m_useConerr) { + inheritHandles = TRUE; + sui.dwFlags |= STARTF_USESTDHANDLES; + sui.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + sui.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + sui.hStdError = m_errorBuffer->conout(); + } + + const BOOL success = + CreateProcessW(programArg, cmdlineArg, nullptr, nullptr, + /*bInheritHandles=*/inheritHandles, + /*dwCreationFlags=*/CREATE_UNICODE_ENVIRONMENT, + envArg, cwdArg, &sui, &pi); + const int lastError = success ? 0 : GetLastError(); + + trace("CreateProcess: %s %u", + (success ? "success" : "fail"), + static_cast<unsigned int>(pi.dwProcessId)); + + auto reply = newPacket(); + if (success) { + int64_t replyProcess = 0; + int64_t replyThread = 0; + if (wantProcessHandle) { + replyProcess = int64FromHandle(duplicateHandle(pi.hProcess)); + } + if (wantThreadHandle) { + replyThread = int64FromHandle(duplicateHandle(pi.hThread)); + } + CloseHandle(pi.hThread); + m_childProcess = pi.hProcess; + m_autoShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN) != 0; + m_exitAfterShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN) != 0; + reply.putInt32(static_cast<int32_t>(StartProcessResult::ProcessCreated)); + reply.putInt64(replyProcess); + reply.putInt64(replyThread); + } else { + reply.putInt32(static_cast<int32_t>(StartProcessResult::CreateProcessFailed)); + reply.putInt32(lastError); + } + writePacket(reply); +} + +void Agent::handleSetSizePacket(ReadBuffer &packet) +{ + const int cols = packet.getInt32(); + const int rows = packet.getInt32(); + packet.assertEof(); + resizeWindow(cols, rows); + auto reply = newPacket(); + writePacket(reply); +} + +void Agent::handleGetConsoleProcessListPacket(ReadBuffer &packet) +{ + packet.assertEof(); + + auto processList = std::vector<DWORD>(64); + auto processCount = GetConsoleProcessList(&processList[0], processList.size()); + + // The process list can change while we're trying to read it + while (processList.size() < processCount) { + // Multiplying by two caps the number of iterations + const auto newSize = std::max<DWORD>(processList.size() * 2, processCount); + processList.resize(newSize); + processCount = GetConsoleProcessList(&processList[0], processList.size()); + } + + if (processCount == 0) { + trace("GetConsoleProcessList failed"); + } + + auto reply = newPacket(); + reply.putInt32(processCount); + for (DWORD i = 0; i < processCount; i++) { + reply.putInt32(processList[i]); + } + writePacket(reply); +} + +void Agent::pollConinPipe() +{ + const std::string newData = m_coninPipe->readAllToString(); + if (hasDebugFlag("input_separated_bytes")) { + // This debug flag is intended to help with testing incomplete escape + // sequences and multibyte UTF-8 encodings. (I wonder if the normal + // code path ought to advance a state machine one byte at a time.) + for (size_t i = 0; i < newData.size(); ++i) { + m_consoleInput->writeInput(newData.substr(i, 1)); + } + } else { + m_consoleInput->writeInput(newData); + } +} + +void Agent::onPollTimeout() +{ + m_consoleInput->updateInputFlags(); + const bool enableMouseMode = m_consoleInput->shouldActivateTerminalMouse(); + + // Give the ConsoleInput object a chance to flush input from an incomplete + // escape sequence (e.g. pressing ESC). + m_consoleInput->flushIncompleteEscapeCode(); + + const bool shouldScrapeContent = !m_closingOutputPipes; + + // Check if the child process has exited. + if (m_autoShutdown && + m_childProcess != nullptr && + WaitForSingleObject(m_childProcess, 0) == WAIT_OBJECT_0) { + CloseHandle(m_childProcess); + m_childProcess = nullptr; + + // Close the data socket to signal to the client that the child + // process has exited. If there's any data left to send, send it + // before closing the socket. + m_closingOutputPipes = true; + } + + // Scrape for output *after* the above exit-check to ensure that we collect + // the child process's final output. + if (shouldScrapeContent) { + syncConsoleTitle(); + scrapeBuffers(); + } + + // We must ensure that we disable mouse mode before closing the CONOUT + // pipe, so update the mouse mode here. + m_primaryScraper->terminal().enableMouseMode( + enableMouseMode && !m_closingOutputPipes); + + autoClosePipesForShutdown(); +} + +void Agent::autoClosePipesForShutdown() +{ + if (m_closingOutputPipes) { + // We don't want to close a pipe before it's connected! If we do, the + // libwinpty client may try to connect to a non-existent pipe. This + // case is important for short-lived programs. + if (m_conoutPipe->isConnected() && + m_conoutPipe->bytesToSend() == 0) { + trace("Closing CONOUT pipe (auto-shutdown)"); + m_conoutPipe->closePipe(); + } + if (m_conerrPipe != nullptr && + m_conerrPipe->isConnected() && + m_conerrPipe->bytesToSend() == 0) { + trace("Closing CONERR pipe (auto-shutdown)"); + m_conerrPipe->closePipe(); + } + if (m_exitAfterShutdown && + m_conoutPipe->isClosed() && + (m_conerrPipe == nullptr || m_conerrPipe->isClosed())) { + trace("Agent exiting (exit-after-shutdown)"); + shutdown(); + } + } +} + +std::unique_ptr<Win32ConsoleBuffer> Agent::openPrimaryBuffer() +{ + // If we're using a separate buffer for stderr, and a program were to + // activate the stderr buffer, then we could accidentally scrape the same + // buffer twice. That probably shouldn't happen in ordinary use, but it + // can be avoided anyway by using the original console screen buffer in + // that mode. + if (!m_useConerr) { + return Win32ConsoleBuffer::openConout(); + } else { + return Win32ConsoleBuffer::openStdout(); + } +} + +void Agent::resizeWindow(int cols, int rows) +{ + ASSERT(cols >= 1 && rows >= 1); + cols = std::min(cols, MAX_CONSOLE_WIDTH); + rows = std::min(rows, MAX_CONSOLE_HEIGHT); + + Win32Console::FreezeGuard guard(m_console, m_console.frozen()); + const Coord newSize(cols, rows); + ConsoleScreenBufferInfo info; + auto primaryBuffer = openPrimaryBuffer(); + m_primaryScraper->resizeWindow(*primaryBuffer, newSize, info); + m_consoleInput->setMouseWindowRect(info.windowRect()); + if (m_errorScraper) { + m_errorScraper->resizeWindow(*m_errorBuffer, newSize, info); + } + + // Synthesize a WINDOW_BUFFER_SIZE_EVENT event. Normally, Windows + // generates this event only when the buffer size changes, not when the + // window size changes. This behavior is undesirable in two ways: + // - When winpty expands the window horizontally, it must expand the + // buffer first, then the window. At least some programs (e.g. the WSL + // bash.exe wrapper) use the window width rather than the buffer width, + // so there is a short timespan during which they can read the wrong + // value. + // - If the window's vertical size is changed, no event is generated, + // even though a typical well-behaved console program cares about the + // *window* height, not the *buffer* height. + // This synthesization works around a design flaw in the console. It's probably + // harmless. See https://github.com/rprichard/winpty/issues/110. + INPUT_RECORD sizeEvent {}; + sizeEvent.EventType = WINDOW_BUFFER_SIZE_EVENT; + sizeEvent.Event.WindowBufferSizeEvent.dwSize = primaryBuffer->bufferSize(); + DWORD actual {}; + WriteConsoleInputW(GetStdHandle(STD_INPUT_HANDLE), &sizeEvent, 1, &actual); +} + +void Agent::scrapeBuffers() +{ + Win32Console::FreezeGuard guard(m_console, m_console.frozen()); + ConsoleScreenBufferInfo info; + m_primaryScraper->scrapeBuffer(*openPrimaryBuffer(), info); + m_consoleInput->setMouseWindowRect(info.windowRect()); + if (m_errorScraper) { + m_errorScraper->scrapeBuffer(*m_errorBuffer, info); + } +} + +void Agent::syncConsoleTitle() +{ + std::wstring newTitle = m_console.title(); + if (newTitle != m_currentTitle) { + if (!m_plainMode && !m_conoutPipe->isClosed()) { + std::string command = std::string("\x1b]0;") + + utf8FromWide(newTitle) + "\x07"; + m_conoutPipe->write(command.c_str()); + } + m_currentTitle = newTitle; + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/Agent.h b/src/libs/3rdparty/winpty/src/agent/Agent.h new file mode 100644 index 0000000000..1dde48fe4a --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Agent.h @@ -0,0 +1,103 @@ +// Copyright (c) 2011-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_H +#define AGENT_H + +#include <windows.h> +#include <stdint.h> + +#include <memory> +#include <string> + +#include "DsrSender.h" +#include "EventLoop.h" +#include "Win32Console.h" + +class ConsoleInput; +class NamedPipe; +class ReadBuffer; +class Scraper; +class WriteBuffer; +class Win32ConsoleBuffer; + +class Agent : public EventLoop, public DsrSender +{ +public: + Agent(LPCWSTR controlPipeName, + uint64_t agentFlags, + int mouseMode, + int initialCols, + int initialRows); + virtual ~Agent(); + void sendDsr() override; + +private: + NamedPipe &connectToControlPipe(LPCWSTR pipeName); + NamedPipe &createDataServerPipe(bool write, const wchar_t *kind); + +private: + void pollControlPipe(); + void handlePacket(ReadBuffer &packet); + void writePacket(WriteBuffer &packet); + void handleStartProcessPacket(ReadBuffer &packet); + void handleSetSizePacket(ReadBuffer &packet); + void handleGetConsoleProcessListPacket(ReadBuffer &packet); + void pollConinPipe(); + +protected: + virtual void onPollTimeout() override; + virtual void onPipeIo(NamedPipe &namedPipe) override; + +private: + void autoClosePipesForShutdown(); + std::unique_ptr<Win32ConsoleBuffer> openPrimaryBuffer(); + void resizeWindow(int cols, int rows); + void scrapeBuffers(); + void syncConsoleTitle(); + +private: + const bool m_useConerr; + const bool m_plainMode; + const int m_mouseMode; + Win32Console m_console; + std::unique_ptr<Scraper> m_primaryScraper; + std::unique_ptr<Scraper> m_errorScraper; + std::unique_ptr<Win32ConsoleBuffer> m_errorBuffer; + NamedPipe *m_controlPipe = nullptr; + NamedPipe *m_coninPipe = nullptr; + NamedPipe *m_conoutPipe = nullptr; + NamedPipe *m_conerrPipe = nullptr; + bool m_autoShutdown = false; + bool m_exitAfterShutdown = false; + bool m_closingOutputPipes = false; + std::unique_ptr<ConsoleInput> m_consoleInput; + HANDLE m_childProcess = nullptr; + + // If the title is initialized to the empty string, then cmd.exe will + // sometimes print this error: + // Not enough storage is available to process this command. + // It happens on Windows 7 when logged into a Cygwin SSH session, for + // example. Using a title of a single space character avoids the problem. + // See https://github.com/rprichard/winpty/issues/74. + std::wstring m_currentTitle = L" "; +}; + +#endif // AGENT_H diff --git a/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc new file mode 100644 index 0000000000..9ad6503b1c --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2016 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 "AgentCreateDesktop.h" + +#include "../shared/BackgroundDesktop.h" +#include "../shared/Buffer.h" +#include "../shared/DebugClient.h" +#include "../shared/StringUtil.h" + +#include "EventLoop.h" +#include "NamedPipe.h" + +namespace { + +static inline WriteBuffer newPacket() { + WriteBuffer packet; + packet.putRawValue<uint64_t>(0); // Reserve space for size. + return packet; +} + +class CreateDesktopLoop : public EventLoop { +public: + CreateDesktopLoop(LPCWSTR controlPipeName); + +protected: + virtual void onPipeIo(NamedPipe &namedPipe) override; + +private: + void writePacket(WriteBuffer &packet); + + BackgroundDesktop m_desktop; + NamedPipe &m_pipe; +}; + +CreateDesktopLoop::CreateDesktopLoop(LPCWSTR controlPipeName) : + m_pipe(createNamedPipe()) { + m_pipe.connectToServer(controlPipeName, NamedPipe::OpenMode::Duplex); + auto packet = newPacket(); + packet.putWString(m_desktop.desktopName()); + writePacket(packet); +} + +void CreateDesktopLoop::writePacket(WriteBuffer &packet) { + const auto &bytes = packet.buf(); + packet.replaceRawValue<uint64_t>(0, bytes.size()); + m_pipe.write(bytes.data(), bytes.size()); +} + +void CreateDesktopLoop::onPipeIo(NamedPipe &namedPipe) { + if (m_pipe.isClosed()) { + shutdown(); + } +} + +} // anonymous namespace + +void handleCreateDesktop(LPCWSTR controlPipeName) { + try { + CreateDesktopLoop loop(controlPipeName); + loop.run(); + trace("Agent exiting..."); + } catch (const WinptyException &e) { + trace("handleCreateDesktop: internal error: %s", + utf8FromWide(e.what()).c_str()); + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h new file mode 100644 index 0000000000..2ae539c7fa --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h @@ -0,0 +1,28 @@ +// Copyright (c) 2016 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_CREATE_DESKTOP_H +#define AGENT_CREATE_DESKTOP_H + +#include <windows.h> + +void handleCreateDesktop(LPCWSTR controlPipeName); + +#endif // AGENT_CREATE_DESKTOP_H diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc new file mode 100644 index 0000000000..aa1f7876d3 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc @@ -0,0 +1,698 @@ +// 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 "ConsoleFont.h" + +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <wchar.h> + +#include <algorithm> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "../shared/DebugClient.h" +#include "../shared/OsModule.h" +#include "../shared/StringUtil.h" +#include "../shared/WindowsVersion.h" +#include "../shared/WinptyAssert.h" +#include "../shared/winpty_snprintf.h" + +namespace { + +#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0])) + +// See https://en.wikipedia.org/wiki/List_of_CJK_fonts +const wchar_t kLucidaConsole[] = L"Lucida Console"; +const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // 932, Japanese +const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // 936, Chinese Simplified +const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // 949, Korean +const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // 950, Chinese Traditional + +struct FontSize { + short size; + int width; +}; + +struct Font { + const wchar_t *faceName; + unsigned int family; + short size; +}; + +// Ideographs in East Asian languages take two columns rather than one. +// In the console screen buffer, a "full-width" character will occupy two +// cells of the buffer, the first with attribute 0x100 and the second with +// attribute 0x200. +// +// Windows does not correctly identify code points as double-width in all +// configurations. It depends heavily on the code page, the font facename, +// and (somehow) even the font size. In the 437 code page (MS-DOS), for +// example, no codepoints are interpreted as double-width. When the console +// is in an East Asian code page (932, 936, 949, or 950), then sometimes +// selecting a "Western" facename like "Lucida Console" or "Consolas" doesn't +// register, or if the font *can* be chosen, then the console doesn't handle +// double-width correctly. I tested the double-width handling by writing +// several code points with WriteConsole and checking whether one or two cells +// were filled. +// +// In the Japanese code page (932), Microsoft's default font is MS Gothic. +// MS Gothic double-width handling seems to be broken with console versions +// prior to Windows 10 (including Windows 10's legacy mode), and it's +// especially broken in Windows 8 and 8.1. +// +// Test by running: misc/Utf16Echo A2 A3 2014 3044 30FC 4000 +// +// The first three codepoints are always rendered as half-width with the +// Windows Japanese fonts. (Of these, the first two must be half-width, +// but U+2014 could be either.) The last three are rendered as full-width, +// and they are East_Asian_Width=Wide. +// +// Windows 7 fails by modeling all codepoints as full-width with font +// sizes 22 and above. +// +// Windows 8 gets U+00A2, U+00A3, U+2014, U+30FC, and U+4000 wrong, but +// using a point size not listed in the console properties dialog +// (e.g. "9") is less wrong: +// +// | code point | +// font | 00A2 00A3 2014 3044 30FC 4000 | cell size +// ------------+---------------------------------+---------- +// 8 | F F F F H H | 4x8 +// 9 | F F F F F F | 5x9 +// 16 | F F F F H H | 8x16 +// raster 6x13 | H H H F F H(*) | 6x13 +// +// (*) The Raster Font renders U+4000 as a white box (i.e. an unsupported +// character). +// + +// See: +// - misc/Font-Report-June2016 directory for per-size details +// - misc/font-notes.txt +// - misc/Utf16Echo.cc, misc/FontSurvey.cc, misc/SetFont.cc, misc/GetFont.cc + +const FontSize kLucidaFontSizes[] = { + { 5, 3 }, + { 6, 4 }, + { 8, 5 }, + { 10, 6 }, + { 12, 7 }, + { 14, 8 }, + { 16, 10 }, + { 18, 11 }, + { 20, 12 }, + { 36, 22 }, + { 48, 29 }, + { 60, 36 }, + { 72, 43 }, +}; + +// Japanese. Used on Vista and Windows 7. +const FontSize k932GothicVista[] = { + { 6, 3 }, + { 8, 4 }, + { 10, 5 }, + { 12, 6 }, + { 13, 7 }, + { 15, 8 }, + { 17, 9 }, + { 19, 10 }, + { 21, 11 }, + // All larger fonts are more broken w.r.t. full-size East Asian characters. +}; + +// Japanese. Used on Windows 8, 8.1, and the legacy 10 console. +const FontSize k932GothicWin8[] = { + // All of these characters are broken w.r.t. full-size East Asian + // characters, but they're equally broken. + { 5, 3 }, + { 7, 4 }, + { 9, 5 }, + { 11, 6 }, + { 13, 7 }, + { 15, 8 }, + { 17, 9 }, + { 20, 10 }, + { 22, 11 }, + { 24, 12 }, + // include extra-large fonts for small terminals + { 36, 18 }, + { 48, 24 }, + { 60, 30 }, + { 72, 36 }, +}; + +// Japanese. Used on the new Windows 10 console. +const FontSize k932GothicWin10[] = { + { 6, 3 }, + { 8, 4 }, + { 10, 5 }, + { 12, 6 }, + { 14, 7 }, + { 16, 8 }, + { 18, 9 }, + { 20, 10 }, + { 22, 11 }, + { 24, 12 }, + // include extra-large fonts for small terminals + { 36, 18 }, + { 48, 24 }, + { 60, 30 }, + { 72, 36 }, +}; + +// Chinese Simplified. +const FontSize k936SimSun[] = { + { 6, 3 }, + { 8, 4 }, + { 10, 5 }, + { 12, 6 }, + { 14, 7 }, + { 16, 8 }, + { 18, 9 }, + { 20, 10 }, + { 22, 11 }, + { 24, 12 }, + // include extra-large fonts for small terminals + { 36, 18 }, + { 48, 24 }, + { 60, 30 }, + { 72, 36 }, +}; + +// Korean. +const FontSize k949GulimChe[] = { + { 6, 3 }, + { 8, 4 }, + { 10, 5 }, + { 12, 6 }, + { 14, 7 }, + { 16, 8 }, + { 18, 9 }, + { 20, 10 }, + { 22, 11 }, + { 24, 12 }, + // include extra-large fonts for small terminals + { 36, 18 }, + { 48, 24 }, + { 60, 30 }, + { 72, 36 }, +}; + +// Chinese Traditional. +const FontSize k950MingLight[] = { + { 6, 3 }, + { 8, 4 }, + { 10, 5 }, + { 12, 6 }, + { 14, 7 }, + { 16, 8 }, + { 18, 9 }, + { 20, 10 }, + { 22, 11 }, + { 24, 12 }, + // include extra-large fonts for small terminals + { 36, 18 }, + { 48, 24 }, + { 60, 30 }, + { 72, 36 }, +}; + +// Some of these types and functions are missing from the MinGW headers. +// Others are undocumented. + +struct AGENT_CONSOLE_FONT_INFO { + DWORD nFont; + COORD dwFontSize; +}; + +struct AGENT_CONSOLE_FONT_INFOEX { + ULONG cbSize; + DWORD nFont; + COORD dwFontSize; + UINT FontFamily; + UINT FontWeight; + WCHAR FaceName[LF_FACESIZE]; +}; + +// undocumented XP API +typedef BOOL WINAPI SetConsoleFont_t( + HANDLE hOutput, + DWORD dwFontIndex); + +// undocumented XP API +typedef DWORD WINAPI GetNumberOfConsoleFonts_t(); + +// XP and up +typedef BOOL WINAPI GetCurrentConsoleFont_t( + HANDLE hOutput, + BOOL bMaximumWindow, + AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont); + +// XP and up +typedef COORD WINAPI GetConsoleFontSize_t( + HANDLE hConsoleOutput, + DWORD nFont); + +// Vista and up +typedef BOOL WINAPI GetCurrentConsoleFontEx_t( + HANDLE hConsoleOutput, + BOOL bMaximumWindow, + AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx); + +// Vista and up +typedef BOOL WINAPI SetCurrentConsoleFontEx_t( + HANDLE hConsoleOutput, + BOOL bMaximumWindow, + AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx); + +#define GET_MODULE_PROC(mod, funcName) \ + m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \ + +#define DEFINE_ACCESSOR(funcName) \ + funcName##_t &funcName() const { \ + ASSERT(valid()); \ + return *m_##funcName; \ + } + +class XPFontAPI { +public: + XPFontAPI() : m_kernel32(L"kernel32.dll") { + GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont); + GET_MODULE_PROC(m_kernel32, GetConsoleFontSize); + } + + bool valid() const { + return m_GetCurrentConsoleFont != NULL && + m_GetConsoleFontSize != NULL; + } + + DEFINE_ACCESSOR(GetCurrentConsoleFont) + DEFINE_ACCESSOR(GetConsoleFontSize) + +private: + OsModule m_kernel32; + GetCurrentConsoleFont_t *m_GetCurrentConsoleFont; + GetConsoleFontSize_t *m_GetConsoleFontSize; +}; + +class UndocumentedXPFontAPI : public XPFontAPI { +public: + UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") { + GET_MODULE_PROC(m_kernel32, SetConsoleFont); + GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts); + } + + bool valid() const { + return this->XPFontAPI::valid() && + m_SetConsoleFont != NULL && + m_GetNumberOfConsoleFonts != NULL; + } + + DEFINE_ACCESSOR(SetConsoleFont) + DEFINE_ACCESSOR(GetNumberOfConsoleFonts) + +private: + OsModule m_kernel32; + SetConsoleFont_t *m_SetConsoleFont; + GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts; +}; + +class VistaFontAPI : public XPFontAPI { +public: + VistaFontAPI() : m_kernel32(L"kernel32.dll") { + GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx); + GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx); + } + + bool valid() const { + return this->XPFontAPI::valid() && + m_GetCurrentConsoleFontEx != NULL && + m_SetCurrentConsoleFontEx != NULL; + } + + DEFINE_ACCESSOR(GetCurrentConsoleFontEx) + DEFINE_ACCESSOR(SetCurrentConsoleFontEx) + +private: + OsModule m_kernel32; + GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx; + SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx; +}; + +static std::vector<std::pair<DWORD, COORD> > readFontTable( + XPFontAPI &api, HANDLE conout, DWORD maxCount) { + std::vector<std::pair<DWORD, COORD> > ret; + for (DWORD i = 0; i < maxCount; ++i) { + COORD size = api.GetConsoleFontSize()(conout, i); + if (size.X == 0 && size.Y == 0) { + break; + } + ret.push_back(std::make_pair(i, size)); + } + return ret; +} + +static void dumpFontTable(HANDLE conout, const char *prefix) { + const int kMaxCount = 1000; + if (!isTracingEnabled()) { + return; + } + XPFontAPI api; + if (!api.valid()) { + trace("dumpFontTable: cannot dump font table -- missing APIs"); + return; + } + std::vector<std::pair<DWORD, COORD> > table = + readFontTable(api, conout, kMaxCount); + std::string line; + char tmp[128]; + size_t first = 0; + while (first < table.size()) { + size_t last = std::min(table.size() - 1, first + 10 - 1); + winpty_snprintf(tmp, "%sfonts %02u-%02u:", + prefix, static_cast<unsigned>(first), static_cast<unsigned>(last)); + line = tmp; + for (size_t i = first; i <= last; ++i) { + if (i % 10 == 5) { + line += " - "; + } + winpty_snprintf(tmp, " %2dx%-2d", + table[i].second.X, table[i].second.Y); + line += tmp; + } + trace("%s", line.c_str()); + first = last + 1; + } + if (table.size() == kMaxCount) { + trace("%sfonts: ... stopped reading at %d fonts ...", + prefix, kMaxCount); + } +} + +static std::string stringToCodePoints(const std::wstring &str) { + std::string ret = "("; + for (size_t i = 0; i < str.size(); ++i) { + char tmp[32]; + winpty_snprintf(tmp, "%X", str[i]); + if (ret.size() > 1) { + ret.push_back(' '); + } + ret += tmp; + } + ret.push_back(')'); + return ret; +} + +static void dumpFontInfoEx( + const AGENT_CONSOLE_FONT_INFOEX &infoex, + const char *prefix) { + if (!isTracingEnabled()) { + return; + } + std::wstring faceName(infoex.FaceName, + winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName))); + trace("%snFont=%u dwFontSize=(%d,%d) " + "FontFamily=0x%x FontWeight=%u FaceName=%s %s", + prefix, + static_cast<unsigned>(infoex.nFont), + infoex.dwFontSize.X, infoex.dwFontSize.Y, + infoex.FontFamily, infoex.FontWeight, utf8FromWide(faceName).c_str(), + stringToCodePoints(faceName).c_str()); +} + +static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, const char *prefix) { + if (!isTracingEnabled()) { + return; + } + AGENT_CONSOLE_FONT_INFOEX infoex = {0}; + infoex.cbSize = sizeof(infoex); + if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) { + trace("GetCurrentConsoleFontEx call failed"); + return; + } + dumpFontInfoEx(infoex, prefix); +} + +static void dumpXPFont(XPFontAPI &api, HANDLE conout, const char *prefix) { + if (!isTracingEnabled()) { + return; + } + AGENT_CONSOLE_FONT_INFO info = {0}; + if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) { + trace("GetCurrentConsoleFont call failed"); + return; + } + trace("%snFont=%u dwFontSize=(%d,%d)", + prefix, + static_cast<unsigned>(info.nFont), + info.dwFontSize.X, info.dwFontSize.Y); +} + +static bool setFontVista( + VistaFontAPI &api, + HANDLE conout, + const Font &font) { + AGENT_CONSOLE_FONT_INFOEX infoex = {}; + infoex.cbSize = sizeof(AGENT_CONSOLE_FONT_INFOEX); + infoex.dwFontSize.Y = font.size; + infoex.FontFamily = font.family; + infoex.FontWeight = 400; + winpty_wcsncpy_nul(infoex.FaceName, font.faceName); + dumpFontInfoEx(infoex, "setFontVista: setting font to: "); + if (!api.SetCurrentConsoleFontEx()(conout, FALSE, &infoex)) { + trace("setFontVista: SetCurrentConsoleFontEx call failed"); + return false; + } + memset(&infoex, 0, sizeof(infoex)); + infoex.cbSize = sizeof(infoex); + if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) { + trace("setFontVista: GetCurrentConsoleFontEx call failed"); + return false; + } + if (wcsncmp(infoex.FaceName, font.faceName, + COUNT_OF(infoex.FaceName)) != 0) { + trace("setFontVista: face name was not set"); + dumpFontInfoEx(infoex, "setFontVista: post-call font: "); + return false; + } + // We'd like to verify that the new font size is correct, but we can't + // predict what it will be, even though we just set it to `pxSize` through + // an apprently symmetric interface. For the Chinese and Korean fonts, the + // new `infoex.dwFontSize.Y` value can be slightly larger than the height + // we specified. + return true; +} + +static Font selectSmallFont(int codePage, int columns, bool isNewW10) { + // Iterate over a set of font sizes according to the code page, and select + // one. + + const wchar_t *faceName = nullptr; + unsigned int fontFamily = 0; + const FontSize *table = nullptr; + size_t tableSize = 0; + + switch (codePage) { + case 932: // Japanese + faceName = kMSGothic; + fontFamily = 0x36; + if (isNewW10) { + table = k932GothicWin10; + tableSize = COUNT_OF(k932GothicWin10); + } else if (isAtLeastWindows8()) { + table = k932GothicWin8; + tableSize = COUNT_OF(k932GothicWin8); + } else { + table = k932GothicVista; + tableSize = COUNT_OF(k932GothicVista); + } + break; + case 936: // Chinese Simplified + faceName = kNSimSun; + fontFamily = 0x36; + table = k936SimSun; + tableSize = COUNT_OF(k936SimSun); + break; + case 949: // Korean + faceName = kGulimChe; + fontFamily = 0x36; + table = k949GulimChe; + tableSize = COUNT_OF(k949GulimChe); + break; + case 950: // Chinese Traditional + faceName = kMingLight; + fontFamily = 0x36; + table = k950MingLight; + tableSize = COUNT_OF(k950MingLight); + break; + default: + faceName = kLucidaConsole; + fontFamily = 0x36; + table = kLucidaFontSizes; + tableSize = COUNT_OF(kLucidaFontSizes); + break; + } + + size_t bestIndex = static_cast<size_t>(-1); + std::tuple<int, int> bestScore = std::make_tuple(-1, -1); + + // We might want to pick the smallest possible font, because we don't know + // how large the monitor is (and the monitor size can change). We might + // want to pick a larger font to accommodate console programs that resize + // the console on their own, like DOS edit.com, which tends to resize the + // console to 80 columns. + + for (size_t i = 0; i < tableSize; ++i) { + const int width = table[i].width * columns; + + // In general, we'd like to pick a font size where cutting the number + // of columns in half doesn't immediately violate the minimum width + // constraint. (e.g. To run DOS edit.com, a user might resize their + // terminal to ~100 columns so it's big enough to show the 80 columns + // post-resize.) To achieve this, give priority to fonts that allow + // this halving. We don't want to encourage *very* large fonts, + // though, so disable the effect as the number of columns scales from + // 80 to 40. + const int halfColumns = std::min(columns, std::max(40, columns / 2)); + const int halfWidth = table[i].width * halfColumns; + + std::tuple<int, int> thisScore = std::make_tuple(-1, -1); + if (width >= 160 && halfWidth >= 160) { + // Both sizes are good. Prefer the smaller fonts. + thisScore = std::make_tuple(2, -width); + } else if (width >= 160) { + // Prefer the smaller fonts. + thisScore = std::make_tuple(1, -width); + } else { + // Otherwise, prefer the largest font in our table. + thisScore = std::make_tuple(0, width); + } + if (thisScore > bestScore) { + bestIndex = i; + bestScore = thisScore; + } + } + + ASSERT(bestIndex != static_cast<size_t>(-1)); + return Font { faceName, fontFamily, table[bestIndex].size }; +} + +static void setSmallFontVista(VistaFontAPI &api, HANDLE conout, + int columns, bool isNewW10) { + int codePage = GetConsoleOutputCP(); + const auto font = selectSmallFont(codePage, columns, isNewW10); + if (setFontVista(api, conout, font)) { + trace("setSmallFontVista: success"); + return; + } + if (codePage == 932 || codePage == 936 || + codePage == 949 || codePage == 950) { + trace("setSmallFontVista: falling back to default codepage font instead"); + const auto fontFB = selectSmallFont(0, columns, isNewW10); + if (setFontVista(api, conout, fontFB)) { + trace("setSmallFontVista: fallback was successful"); + return; + } + } + trace("setSmallFontVista: failure"); +} + +struct FontSizeComparator { + bool operator()(const std::pair<DWORD, COORD> &obj1, + const std::pair<DWORD, COORD> &obj2) const { + int score1 = obj1.second.X + obj1.second.Y; + int score2 = obj2.second.X + obj2.second.Y; + return score1 < score2; + } +}; + +static void setSmallFontXP(UndocumentedXPFontAPI &api, HANDLE conout) { + // Read the console font table and sort it from smallest to largest. + const DWORD fontCount = api.GetNumberOfConsoleFonts()(); + trace("setSmallFontXP: number of console fonts: %u", + static_cast<unsigned>(fontCount)); + std::vector<std::pair<DWORD, COORD> > table = + readFontTable(api, conout, fontCount); + std::sort(table.begin(), table.end(), FontSizeComparator()); + for (size_t i = 0; i < table.size(); ++i) { + // Skip especially narrow fonts to permit narrower terminals. + if (table[i].second.X < 4) { + continue; + } + trace("setSmallFontXP: setting font to %u", + static_cast<unsigned>(table[i].first)); + if (!api.SetConsoleFont()(conout, table[i].first)) { + trace("setSmallFontXP: SetConsoleFont call failed"); + continue; + } + AGENT_CONSOLE_FONT_INFO info; + if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) { + trace("setSmallFontXP: GetCurrentConsoleFont call failed"); + return; + } + if (info.nFont != table[i].first) { + trace("setSmallFontXP: font was not set"); + dumpXPFont(api, conout, "setSmallFontXP: post-call font: "); + continue; + } + trace("setSmallFontXP: success"); + return; + } + trace("setSmallFontXP: failure"); +} + +} // anonymous namespace + +// A Windows console window can never be larger than the desktop window. To +// maximize the possible size of the console in rows*cols, try to configure +// the console with a small font. Unfortunately, we cannot make the font *too* +// small, because there is also a minimum window size in pixels. +void setSmallFont(HANDLE conout, int columns, bool isNewW10) { + trace("setSmallFont: attempting to set a small font for %d columns " + "(CP=%u OutputCP=%u)", + columns, + static_cast<unsigned>(GetConsoleCP()), + static_cast<unsigned>(GetConsoleOutputCP())); + VistaFontAPI vista; + if (vista.valid()) { + dumpVistaFont(vista, conout, "previous font: "); + dumpFontTable(conout, "previous font table: "); + setSmallFontVista(vista, conout, columns, isNewW10); + dumpVistaFont(vista, conout, "new font: "); + dumpFontTable(conout, "new font table: "); + return; + } + UndocumentedXPFontAPI xp; + if (xp.valid()) { + dumpXPFont(xp, conout, "previous font: "); + dumpFontTable(conout, "previous font table: "); + setSmallFontXP(xp, conout); + dumpXPFont(xp, conout, "new font: "); + dumpFontTable(conout, "new font table: "); + return; + } + trace("setSmallFont: neither Vista nor XP APIs detected -- giving up"); + dumpFontTable(conout, "font table: "); +} diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h new file mode 100644 index 0000000000..99cb10698d --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h @@ -0,0 +1,28 @@ +// 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 CONSOLEFONT_H +#define CONSOLEFONT_H + +#include <windows.h> + +void setSmallFont(HANDLE conout, int columns, bool isNewW10); + +#endif // CONSOLEFONT_H diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc new file mode 100644 index 0000000000..192cac2a29 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc @@ -0,0 +1,852 @@ +// Copyright (c) 2011-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 "ConsoleInput.h" + +#include <stdio.h> +#include <string.h> + +#include <algorithm> +#include <string> + +#include "../include/winpty_constants.h" + +#include "../shared/DebugClient.h" +#include "../shared/StringBuilder.h" +#include "../shared/UnixCtrlChars.h" + +#include "ConsoleInputReencoding.h" +#include "DebugShowInput.h" +#include "DefaultInputMap.h" +#include "DsrSender.h" +#include "UnicodeEncoding.h" +#include "Win32Console.h" + +// MAPVK_VK_TO_VSC isn't defined by the old MinGW. +#ifndef MAPVK_VK_TO_VSC +#define MAPVK_VK_TO_VSC 0 +#endif + +namespace { + +struct MouseRecord { + bool release; + int flags; + COORD coord; + + std::string toString() const; +}; + +std::string MouseRecord::toString() const { + StringBuilder sb(40); + sb << "pos=" << coord.X << ',' << coord.Y + << " flags=0x" << hexOfInt(flags); + if (release) { + sb << " release"; + } + return sb.str_moved(); +} + +const unsigned int kIncompleteEscapeTimeoutMs = 1000u; + +#define CHECK(cond) \ + do { \ + if (!(cond)) { return 0; } \ + } while(0) + +#define ADVANCE() \ + do { \ + pch++; \ + if (pch == stop) { return -1; } \ + } while(0) + +#define SCAN_INT(out, maxLen) \ + do { \ + (out) = 0; \ + CHECK(isdigit(*pch)); \ + const char *begin = pch; \ + do { \ + CHECK(pch - begin + 1 < maxLen); \ + (out) = (out) * 10 + *pch - '0'; \ + ADVANCE(); \ + } while (isdigit(*pch)); \ + } while(0) + +#define SCAN_SIGNED_INT(out, maxLen) \ + do { \ + bool negative = false; \ + if (*pch == '-') { \ + negative = true; \ + ADVANCE(); \ + } \ + SCAN_INT(out, maxLen); \ + if (negative) { \ + (out) = -(out); \ + } \ + } while(0) + +// Match the Device Status Report console input: ESC [ nn ; mm R +// Returns: +// 0 no match +// >0 match, returns length of match +// -1 incomplete match +static int matchDsr(const char *input, int inputSize) +{ + int32_t dummy = 0; + const char *pch = input; + const char *stop = input + inputSize; + CHECK(*pch == '\x1B'); ADVANCE(); + CHECK(*pch == '['); ADVANCE(); + SCAN_INT(dummy, 8); + CHECK(*pch == ';'); ADVANCE(); + SCAN_INT(dummy, 8); + CHECK(*pch == 'R'); + return pch - input + 1; +} + +static int matchMouseDefault(const char *input, int inputSize, + MouseRecord &out) +{ + const char *pch = input; + const char *stop = input + inputSize; + CHECK(*pch == '\x1B'); ADVANCE(); + CHECK(*pch == '['); ADVANCE(); + CHECK(*pch == 'M'); ADVANCE(); + out.flags = (*pch - 32) & 0xFF; ADVANCE(); + out.coord.X = (*pch - '!') & 0xFF; + ADVANCE(); + out.coord.Y = (*pch - '!') & 0xFF; + out.release = false; + return pch - input + 1; +} + +static int matchMouse1006(const char *input, int inputSize, MouseRecord &out) +{ + const char *pch = input; + const char *stop = input + inputSize; + int32_t temp; + CHECK(*pch == '\x1B'); ADVANCE(); + CHECK(*pch == '['); ADVANCE(); + CHECK(*pch == '<'); ADVANCE(); + SCAN_INT(out.flags, 8); + CHECK(*pch == ';'); ADVANCE(); + SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1; + CHECK(*pch == ';'); ADVANCE(); + SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1; + CHECK(*pch == 'M' || *pch == 'm'); + out.release = (*pch == 'm'); + return pch - input + 1; +} + +static int matchMouse1015(const char *input, int inputSize, MouseRecord &out) +{ + const char *pch = input; + const char *stop = input + inputSize; + int32_t temp; + CHECK(*pch == '\x1B'); ADVANCE(); + CHECK(*pch == '['); ADVANCE(); + SCAN_INT(out.flags, 8); out.flags -= 32; + CHECK(*pch == ';'); ADVANCE(); + SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1; + CHECK(*pch == ';'); ADVANCE(); + SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1; + CHECK(*pch == 'M'); + out.release = false; + return pch - input + 1; +} + +// Match a mouse input escape sequence of any kind. +// 0 no match +// >0 match, returns length of match +// -1 incomplete match +static int matchMouseRecord(const char *input, int inputSize, MouseRecord &out) +{ + memset(&out, 0, sizeof(out)); + int ret; + if ((ret = matchMouse1006(input, inputSize, out)) != 0) { return ret; } + if ((ret = matchMouse1015(input, inputSize, out)) != 0) { return ret; } + if ((ret = matchMouseDefault(input, inputSize, out)) != 0) { return ret; } + return 0; +} + +#undef CHECK +#undef ADVANCE +#undef SCAN_INT + +} // anonymous namespace + +ConsoleInput::ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender, + Win32Console &console) : + m_console(console), + m_conin(conin), + m_mouseMode(mouseMode), + m_dsrSender(dsrSender) +{ + addDefaultEntriesToInputMap(m_inputMap); + if (hasDebugFlag("dump_input_map")) { + m_inputMap.dumpInputMap(); + } + + // Configure Quick Edit mode according to the mouse mode. Enable + // InsertMode for two reasons: + // - If it's OFF, it's difficult for the user to turn it ON. The + // properties dialog is inaccesible. winpty still faithfully handles + // the Insert key, which toggles between the insertion and overwrite + // modes. + // - When we modify the QuickEdit setting, if ExtendedFlags is OFF, + // then we must choose the InsertMode setting. I don't *think* this + // case happens, though, because a new console always has ExtendedFlags + // ON. + // See misc/EnableExtendedFlags.txt. + DWORD mode = 0; + if (!GetConsoleMode(conin, &mode)) { + trace("Agent startup: GetConsoleMode failed"); + } else { + mode |= ENABLE_EXTENDED_FLAGS; + mode |= ENABLE_INSERT_MODE; + if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) { + mode |= ENABLE_QUICK_EDIT_MODE; + } else { + mode &= ~ENABLE_QUICK_EDIT_MODE; + } + if (!SetConsoleMode(conin, mode)) { + trace("Agent startup: SetConsoleMode failed"); + } + } + + updateInputFlags(true); +} + +void ConsoleInput::writeInput(const std::string &input) +{ + if (input.size() == 0) { + return; + } + + if (isTracingEnabled()) { + static bool debugInput = hasDebugFlag("input"); + if (debugInput) { + std::string dumpString; + for (size_t i = 0; i < input.size(); ++i) { + const char ch = input[i]; + const char ctrl = decodeUnixCtrlChar(ch); + if (ctrl != '\0') { + dumpString += '^'; + dumpString += ctrl; + } else { + dumpString += ch; + } + } + dumpString += " ("; + for (size_t i = 0; i < input.size(); ++i) { + if (i > 0) { + dumpString += ' '; + } + const unsigned char uch = input[i]; + char buf[32]; + winpty_snprintf(buf, "%02X", uch); + dumpString += buf; + } + dumpString += ')'; + trace("input chars: %s", dumpString.c_str()); + } + } + + m_byteQueue.append(input); + doWrite(false); + if (!m_byteQueue.empty() && !m_dsrSent) { + trace("send DSR"); + m_dsrSender.sendDsr(); + m_dsrSent = true; + } + m_lastWriteTick = GetTickCount(); +} + +void ConsoleInput::flushIncompleteEscapeCode() +{ + if (!m_byteQueue.empty() && + (GetTickCount() - m_lastWriteTick) > kIncompleteEscapeTimeoutMs) { + doWrite(true); + m_byteQueue.clear(); + } +} + +void ConsoleInput::updateInputFlags(bool forceTrace) +{ + const DWORD mode = inputConsoleMode(); + const bool newFlagEE = (mode & ENABLE_EXTENDED_FLAGS) != 0; + const bool newFlagMI = (mode & ENABLE_MOUSE_INPUT) != 0; + const bool newFlagQE = (mode & ENABLE_QUICK_EDIT_MODE) != 0; + const bool newFlagEI = (mode & 0x200) != 0; + if (forceTrace || + newFlagEE != m_enableExtendedEnabled || + newFlagMI != m_mouseInputEnabled || + newFlagQE != m_quickEditEnabled || + newFlagEI != m_escapeInputEnabled) { + trace("CONIN modes: Extended=%s, MouseInput=%s QuickEdit=%s EscapeInput=%s", + newFlagEE ? "on" : "off", + newFlagMI ? "on" : "off", + newFlagQE ? "on" : "off", + newFlagEI ? "on" : "off"); + } + m_enableExtendedEnabled = newFlagEE; + m_mouseInputEnabled = newFlagMI; + m_quickEditEnabled = newFlagQE; + m_escapeInputEnabled = newFlagEI; +} + +bool ConsoleInput::shouldActivateTerminalMouse() +{ + // Return whether the agent should activate the terminal's mouse mode. + if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) { + // Some programs (e.g. Cygwin command-line programs like bash.exe and + // python2.7.exe) turn off ENABLE_EXTENDED_FLAGS and turn on + // ENABLE_MOUSE_INPUT, but do not turn off QuickEdit mode and do not + // actually care about mouse input. Only enable the terminal mouse + // mode if ENABLE_EXTENDED_FLAGS is on. See + // misc/EnableExtendedFlags.txt. + return m_mouseInputEnabled && !m_quickEditEnabled && + m_enableExtendedEnabled; + } else if (m_mouseMode == WINPTY_MOUSE_MODE_FORCE) { + return true; + } else { + return false; + } +} + +void ConsoleInput::doWrite(bool isEof) +{ + const char *data = m_byteQueue.c_str(); + std::vector<INPUT_RECORD> records; + size_t idx = 0; + while (idx < m_byteQueue.size()) { + int charSize = scanInput(records, &data[idx], m_byteQueue.size() - idx, isEof); + if (charSize == -1) + break; + idx += charSize; + } + m_byteQueue.erase(0, idx); + flushInputRecords(records); +} + +void ConsoleInput::flushInputRecords(std::vector<INPUT_RECORD> &records) +{ + if (records.size() == 0) { + return; + } + DWORD actual = 0; + if (!WriteConsoleInputW(m_conin, records.data(), records.size(), &actual)) { + trace("WriteConsoleInputW failed"); + } + records.clear(); +} + +// This behavior isn't strictly correct, because the keypresses (probably?) +// adopt the keyboard state (e.g. Ctrl/Alt/Shift modifiers) of the current +// window station's keyboard, which has no necessary relationship to the winpty +// instance. It's unlikely to be an issue in practice, but it's conceivable. +// (Imagine a foreground SSH server, where the local user holds down Ctrl, +// while the remote user tries to use WSL navigation keys.) I suspect using +// the BackgroundDesktop mechanism in winpty would fix the problem. +// +// https://github.com/rprichard/winpty/issues/116 +static void sendKeyMessage(HWND hwnd, bool isKeyDown, uint16_t virtualKey) +{ + uint32_t scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC); + if (scanCode > 255) { + scanCode = 0; + } + SendMessage(hwnd, isKeyDown ? WM_KEYDOWN : WM_KEYUP, virtualKey, + (scanCode << 16) | 1u | (isKeyDown ? 0u : 0xc0000000u)); +} + +int ConsoleInput::scanInput(std::vector<INPUT_RECORD> &records, + const char *input, + int inputSize, + bool isEof) +{ + ASSERT(inputSize >= 1); + + // Ctrl-C. + // + // In processed mode, use GenerateConsoleCtrlEvent so that Ctrl-C handlers + // are called. GenerateConsoleCtrlEvent unfortunately doesn't interrupt + // ReadConsole calls[1]. Using WM_KEYDOWN/UP fixes the ReadConsole + // problem, but breaks in background window stations/desktops. + // + // In unprocessed mode, there's an entry for Ctrl-C in the SimpleEncoding + // table in DefaultInputMap. + // + // [1] https://github.com/rprichard/winpty/issues/116 + if (input[0] == '\x03' && (inputConsoleMode() & ENABLE_PROCESSED_INPUT)) { + flushInputRecords(records); + trace("Ctrl-C"); + const BOOL ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + trace("GenerateConsoleCtrlEvent: %d", ret); + return 1; + } + + if (input[0] == '\x1B') { + // Attempt to match the Device Status Report (DSR) reply. + int dsrLen = matchDsr(input, inputSize); + if (dsrLen > 0) { + trace("Received a DSR reply"); + m_dsrSent = false; + return dsrLen; + } else if (!isEof && dsrLen == -1) { + // Incomplete DSR match. + trace("Incomplete DSR match"); + return -1; + } + + int mouseLen = scanMouseInput(records, input, inputSize); + if (mouseLen > 0 || (!isEof && mouseLen == -1)) { + return mouseLen; + } + } + + // Search the input map. + InputMap::Key match; + bool incomplete; + int matchLen = m_inputMap.lookupKey(input, inputSize, match, incomplete); + if (!isEof && incomplete) { + // Incomplete match -- need more characters (or wait for a + // timeout to signify flushed input). + trace("Incomplete escape sequence"); + return -1; + } else if (matchLen > 0) { + uint32_t winCodePointDn = match.unicodeChar; + if ((match.keyState & LEFT_CTRL_PRESSED) && (match.keyState & LEFT_ALT_PRESSED)) { + winCodePointDn = '\0'; + } + uint32_t winCodePointUp = winCodePointDn; + if (match.keyState & LEFT_ALT_PRESSED) { + winCodePointUp = '\0'; + } + appendKeyPress(records, match.virtualKey, + winCodePointDn, winCodePointUp, match.keyState, + match.unicodeChar, match.keyState); + return matchLen; + } + + // Recognize Alt-<character>. + // + // This code doesn't match Alt-ESC, which is encoded as `ESC ESC`, but + // maybe it should. I was concerned that pressing ESC rapidly enough could + // accidentally trigger Alt-ESC. (e.g. The user would have to be faster + // than the DSR flushing mechanism or use a decrepit terminal. The user + // might be on a slow network connection.) + if (input[0] == '\x1B' && inputSize >= 2 && input[1] != '\x1B') { + const int len = utf8CharLength(input[1]); + if (len > 0) { + if (1 + len > inputSize) { + // Incomplete character. + trace("Incomplete UTF-8 character in Alt-<Char>"); + return -1; + } + appendUtf8Char(records, &input[1], len, true); + return 1 + len; + } + } + + // A UTF-8 character. + const int len = utf8CharLength(input[0]); + if (len == 0) { + static bool debugInput = isTracingEnabled() && hasDebugFlag("input"); + if (debugInput) { + trace("Discarding invalid input byte: %02X", + static_cast<unsigned char>(input[0])); + } + return 1; + } + if (len > inputSize) { + // Incomplete character. + trace("Incomplete UTF-8 character"); + return -1; + } + appendUtf8Char(records, &input[0], len, false); + return len; +} + +int ConsoleInput::scanMouseInput(std::vector<INPUT_RECORD> &records, + const char *input, + int inputSize) +{ + MouseRecord record; + const int len = matchMouseRecord(input, inputSize, record); + if (len <= 0) { + return len; + } + + if (isTracingEnabled()) { + static bool debugInput = hasDebugFlag("input"); + if (debugInput) { + trace("mouse input: %s", record.toString().c_str()); + } + } + + const int button = record.flags & 0x03; + INPUT_RECORD newRecord = {0}; + newRecord.EventType = MOUSE_EVENT; + MOUSE_EVENT_RECORD &mer = newRecord.Event.MouseEvent; + + mer.dwMousePosition.X = + m_mouseWindowRect.Left + + std::max(0, std::min<int>(record.coord.X, + m_mouseWindowRect.width() - 1)); + + mer.dwMousePosition.Y = + m_mouseWindowRect.Top + + std::max(0, std::min<int>(record.coord.Y, + m_mouseWindowRect.height() - 1)); + + // The modifier state is neatly independent of everything else. + if (record.flags & 0x04) { mer.dwControlKeyState |= SHIFT_PRESSED; } + if (record.flags & 0x08) { mer.dwControlKeyState |= LEFT_ALT_PRESSED; } + if (record.flags & 0x10) { mer.dwControlKeyState |= LEFT_CTRL_PRESSED; } + + if (record.flags & 0x40) { + // Mouse wheel + mer.dwEventFlags |= MOUSE_WHEELED; + if (button == 0) { + // up + mer.dwButtonState |= 0x00780000; + } else if (button == 1) { + // down + mer.dwButtonState |= 0xff880000; + } else { + // Invalid -- do nothing + return len; + } + } else { + // Ordinary mouse event + if (record.flags & 0x20) { mer.dwEventFlags |= MOUSE_MOVED; } + if (button == 3) { + m_mouseButtonState = 0; + // Potentially advance double-click detection. + m_doubleClick.released = true; + } else { + const DWORD relevantFlag = + (button == 0) ? FROM_LEFT_1ST_BUTTON_PRESSED : + (button == 1) ? FROM_LEFT_2ND_BUTTON_PRESSED : + (button == 2) ? RIGHTMOST_BUTTON_PRESSED : + 0; + ASSERT(relevantFlag != 0); + if (record.release) { + m_mouseButtonState &= ~relevantFlag; + if (relevantFlag == m_doubleClick.button) { + // Potentially advance double-click detection. + m_doubleClick.released = true; + } else { + // End double-click detection. + m_doubleClick = DoubleClickDetection(); + } + } else if ((m_mouseButtonState & relevantFlag) == 0) { + // The button has been newly pressed. + m_mouseButtonState |= relevantFlag; + // Detect a double-click. This code looks for an exact + // coordinate match, which is stricter than what Windows does, + // but Windows has pixel coordinates, and we only have terminal + // coordinates. + if (m_doubleClick.button == relevantFlag && + m_doubleClick.pos == record.coord && + (GetTickCount() - m_doubleClick.tick < + GetDoubleClickTime())) { + // Record a double-click and end double-click detection. + mer.dwEventFlags |= DOUBLE_CLICK; + m_doubleClick = DoubleClickDetection(); + } else { + // Begin double-click detection. + m_doubleClick.button = relevantFlag; + m_doubleClick.pos = record.coord; + m_doubleClick.tick = GetTickCount(); + } + } + } + } + + mer.dwButtonState |= m_mouseButtonState; + + if (m_mouseInputEnabled && !m_quickEditEnabled) { + if (isTracingEnabled()) { + static bool debugInput = hasDebugFlag("input"); + if (debugInput) { + trace("mouse event: %s", mouseEventToString(mer).c_str()); + } + } + + records.push_back(newRecord); + } + + return len; +} + +void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records, + const char *charBuffer, + const int charLen, + const bool terminalAltEscape) +{ + const uint32_t codePoint = decodeUtf8(charBuffer); + if (codePoint == static_cast<uint32_t>(-1)) { + static bool debugInput = isTracingEnabled() && hasDebugFlag("input"); + if (debugInput) { + StringBuilder error(64); + error << "Discarding invalid UTF-8 sequence:"; + for (int i = 0; i < charLen; ++i) { + error << ' '; + error << hexOfInt<true, uint8_t>(charBuffer[i]); + } + trace("%s", error.c_str()); + } + return; + } + + const short charScan = codePoint > 0xFFFF ? -1 : VkKeyScan(codePoint); + uint16_t virtualKey = 0; + uint16_t winKeyState = 0; + uint32_t winCodePointDn = codePoint; + uint32_t winCodePointUp = codePoint; + uint16_t vtKeyState = 0; + + if (charScan != -1) { + virtualKey = charScan & 0xFF; + if (charScan & 0x100) { + winKeyState |= SHIFT_PRESSED; + } + if (charScan & 0x200) { + winKeyState |= LEFT_CTRL_PRESSED; + } + if (charScan & 0x400) { + winKeyState |= RIGHT_ALT_PRESSED; + } + if (terminalAltEscape && (winKeyState & LEFT_CTRL_PRESSED)) { + // If the terminal escapes a Ctrl-<Key> with Alt, then set the + // codepoint to 0. On the other hand, if a character requires + // AltGr (like U+00B2 on a German layout), then VkKeyScan will + // report both Ctrl and Alt pressed, and we should keep the + // codepoint. See https://github.com/rprichard/winpty/issues/109. + winCodePointDn = 0; + winCodePointUp = 0; + } + } + if (terminalAltEscape) { + winCodePointUp = 0; + winKeyState |= LEFT_ALT_PRESSED; + vtKeyState |= LEFT_ALT_PRESSED; + } + + appendKeyPress(records, virtualKey, + winCodePointDn, winCodePointUp, winKeyState, + codePoint, vtKeyState); +} + +void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records, + const uint16_t virtualKey, + const uint32_t winCodePointDn, + const uint32_t winCodePointUp, + const uint16_t winKeyState, + const uint32_t vtCodePoint, + const uint16_t vtKeyState) +{ + const bool ctrl = (winKeyState & LEFT_CTRL_PRESSED) != 0; + const bool leftAlt = (winKeyState & LEFT_ALT_PRESSED) != 0; + const bool rightAlt = (winKeyState & RIGHT_ALT_PRESSED) != 0; + const bool shift = (winKeyState & SHIFT_PRESSED) != 0; + const bool enhanced = (winKeyState & ENHANCED_KEY) != 0; + bool hasDebugInput = false; + + if (isTracingEnabled()) { + static bool debugInput = hasDebugFlag("input"); + if (debugInput) { + hasDebugInput = true; + InputMap::Key key = { virtualKey, winCodePointDn, winKeyState }; + trace("keypress: %s", key.toString().c_str()); + } + } + + if (m_escapeInputEnabled && + (virtualKey == VK_UP || + virtualKey == VK_DOWN || + virtualKey == VK_LEFT || + virtualKey == VK_RIGHT || + virtualKey == VK_HOME || + virtualKey == VK_END) && + !ctrl && !leftAlt && !rightAlt && !shift) { + flushInputRecords(records); + if (hasDebugInput) { + trace("sending keypress to console HWND"); + } + sendKeyMessage(m_console.hwnd(), true, virtualKey); + sendKeyMessage(m_console.hwnd(), false, virtualKey); + return; + } + + uint16_t stepKeyState = 0; + if (ctrl) { + stepKeyState |= LEFT_CTRL_PRESSED; + appendInputRecord(records, TRUE, VK_CONTROL, 0, stepKeyState); + } + if (leftAlt) { + stepKeyState |= LEFT_ALT_PRESSED; + appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState); + } + if (rightAlt) { + stepKeyState |= RIGHT_ALT_PRESSED; + appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState | ENHANCED_KEY); + } + if (shift) { + stepKeyState |= SHIFT_PRESSED; + appendInputRecord(records, TRUE, VK_SHIFT, 0, stepKeyState); + } + if (enhanced) { + stepKeyState |= ENHANCED_KEY; + } + if (m_escapeInputEnabled) { + reencodeEscapedKeyPress(records, virtualKey, vtCodePoint, vtKeyState); + } else { + appendCPInputRecords(records, TRUE, virtualKey, winCodePointDn, stepKeyState); + } + appendCPInputRecords(records, FALSE, virtualKey, winCodePointUp, stepKeyState); + if (enhanced) { + stepKeyState &= ~ENHANCED_KEY; + } + if (shift) { + stepKeyState &= ~SHIFT_PRESSED; + appendInputRecord(records, FALSE, VK_SHIFT, 0, stepKeyState); + } + if (rightAlt) { + stepKeyState &= ~RIGHT_ALT_PRESSED; + appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState | ENHANCED_KEY); + } + if (leftAlt) { + stepKeyState &= ~LEFT_ALT_PRESSED; + appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState); + } + if (ctrl) { + stepKeyState &= ~LEFT_CTRL_PRESSED; + appendInputRecord(records, FALSE, VK_CONTROL, 0, stepKeyState); + } +} + +void ConsoleInput::appendCPInputRecords(std::vector<INPUT_RECORD> &records, + BOOL keyDown, + uint16_t virtualKey, + uint32_t codePoint, + uint16_t keyState) +{ + // This behavior really doesn't match that of the Windows console (in + // normal, non-escape-mode). Judging by the copy-and-paste behavior, + // Windows apparently handles everything outside of the keyboard layout by + // first sending a sequence of Alt+KeyPad events, then finally a key-up + // event whose UnicodeChar has the appropriate value. For U+00A2 (CENT + // SIGN): + // + // key: dn rpt=1 scn=56 LAlt-MENU ch=0 + // key: dn rpt=1 scn=79 LAlt-NUMPAD1 ch=0 + // key: up rpt=1 scn=79 LAlt-NUMPAD1 ch=0 + // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0 + // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0 + // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0 + // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0 + // key: up rpt=1 scn=56 MENU ch=0xa2 + // + // The Alt+155 value matches the encoding of U+00A2 in CP-437. Curiously, + // if I use "chcp 1252" to change the encoding, then copy-and-pasting + // produces Alt+162 instead. (U+00A2 is 162 in CP-1252.) However, typing + // Alt+155 or Alt+162 produce the same characters regardless of console + // code page. (That is, they use CP-437 and yield U+00A2 and U+00F3.) + // + // For characters outside the BMP, Windows repeats the process for both + // UTF-16 code units, e.g, for U+1F300 (CYCLONE): + // + // key: dn rpt=1 scn=56 LAlt-MENU ch=0 + // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0 + // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0 + // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0 + // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0 + // key: up rpt=1 scn=56 MENU ch=0xd83c + // key: dn rpt=1 scn=56 LAlt-MENU ch=0 + // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0 + // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0 + // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0 + // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0 + // key: up rpt=1 scn=56 MENU ch=0xdf00 + // + // In this case, it sends Alt+63 twice, which signifies '?'. Apparently + // CMD and Cygwin bash are both able to decode this. + // + // Also note that typing Alt+NNN still works if NumLock is off, e.g.: + // + // key: dn rpt=1 scn=56 LAlt-MENU ch=0 + // key: dn rpt=1 scn=79 LAlt-END ch=0 + // key: up rpt=1 scn=79 LAlt-END ch=0 + // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0 + // key: up rpt=1 scn=76 LAlt-CLEAR ch=0 + // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0 + // key: up rpt=1 scn=76 LAlt-CLEAR ch=0 + // key: up rpt=1 scn=56 MENU ch=0xa2 + // + // Evidently, the Alt+NNN key events are not intended to be decoded to a + // character. Maybe programs are looking for a key-up ALT/MENU event with + // a non-zero character? + + wchar_t ws[2]; + const int wslen = encodeUtf16(ws, codePoint); + + if (wslen == 1) { + appendInputRecord(records, keyDown, virtualKey, ws[0], keyState); + } else if (wslen == 2) { + appendInputRecord(records, keyDown, virtualKey, ws[0], keyState); + appendInputRecord(records, keyDown, virtualKey, ws[1], keyState); + } else { + // This situation isn't that bad, but it should never happen, + // because invalid codepoints shouldn't reach this point. + trace("INTERNAL ERROR: appendInputRecordCP: invalid codePoint: " + "U+%04X", codePoint); + } +} + +void ConsoleInput::appendInputRecord(std::vector<INPUT_RECORD> &records, + BOOL keyDown, + uint16_t virtualKey, + wchar_t utf16Char, + uint16_t keyState) +{ + INPUT_RECORD ir = {}; + ir.EventType = KEY_EVENT; + ir.Event.KeyEvent.bKeyDown = keyDown; + ir.Event.KeyEvent.wRepeatCount = 1; + ir.Event.KeyEvent.wVirtualKeyCode = virtualKey; + ir.Event.KeyEvent.wVirtualScanCode = + MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC); + ir.Event.KeyEvent.uChar.UnicodeChar = utf16Char; + ir.Event.KeyEvent.dwControlKeyState = keyState; + records.push_back(ir); +} + +DWORD ConsoleInput::inputConsoleMode() +{ + DWORD mode = 0; + if (!GetConsoleMode(m_conin, &mode)) { + trace("GetConsoleMode failed"); + return 0; + } + return mode; +} diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h new file mode 100644 index 0000000000..e807d973ba --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011-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 CONSOLEINPUT_H +#define CONSOLEINPUT_H + +#include <windows.h> +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "Coord.h" +#include "InputMap.h" +#include "SmallRect.h" + +class Win32Console; +class DsrSender; + +class ConsoleInput +{ +public: + ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender, + Win32Console &console); + void writeInput(const std::string &input); + void flushIncompleteEscapeCode(); + void setMouseWindowRect(SmallRect val) { m_mouseWindowRect = val; } + void updateInputFlags(bool forceTrace=false); + bool shouldActivateTerminalMouse(); + +private: + void doWrite(bool isEof); + void flushInputRecords(std::vector<INPUT_RECORD> &records); + int scanInput(std::vector<INPUT_RECORD> &records, + const char *input, + int inputSize, + bool isEof); + int scanMouseInput(std::vector<INPUT_RECORD> &records, + const char *input, + int inputSize); + void appendUtf8Char(std::vector<INPUT_RECORD> &records, + const char *charBuffer, + int charLen, + bool terminalAltEscape); + void appendKeyPress(std::vector<INPUT_RECORD> &records, + uint16_t virtualKey, + uint32_t winCodePointDn, + uint32_t winCodePointUp, + uint16_t winKeyState, + uint32_t vtCodePoint, + uint16_t vtKeyState); + +public: + static void appendCPInputRecords(std::vector<INPUT_RECORD> &records, + BOOL keyDown, + uint16_t virtualKey, + uint32_t codePoint, + uint16_t keyState); + static void appendInputRecord(std::vector<INPUT_RECORD> &records, + BOOL keyDown, + uint16_t virtualKey, + wchar_t utf16Char, + uint16_t keyState); + +private: + DWORD inputConsoleMode(); + +private: + Win32Console &m_console; + HANDLE m_conin = nullptr; + int m_mouseMode = 0; + DsrSender &m_dsrSender; + bool m_dsrSent = false; + std::string m_byteQueue; + InputMap m_inputMap; + DWORD m_lastWriteTick = 0; + DWORD m_mouseButtonState = 0; + struct DoubleClickDetection { + DWORD button = 0; + Coord pos; + DWORD tick = 0; + bool released = false; + } m_doubleClick; + bool m_enableExtendedEnabled = false; + bool m_mouseInputEnabled = false; + bool m_quickEditEnabled = false; + bool m_escapeInputEnabled = false; + SmallRect m_mouseWindowRect; +}; + +#endif // CONSOLEINPUT_H diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc new file mode 100644 index 0000000000..b79545eea9 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2016 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 "ConsoleInputReencoding.h" + +#include "ConsoleInput.h" + +namespace { + +static void outch(std::vector<INPUT_RECORD> &out, wchar_t ch) { + ConsoleInput::appendInputRecord(out, TRUE, 0, ch, 0); +} + +} // anonymous namespace + +void reencodeEscapedKeyPress( + std::vector<INPUT_RECORD> &out, + uint16_t virtualKey, + uint32_t codePoint, + uint16_t keyState) { + + struct EscapedKey { + enum { None, Numeric, Letter } kind; + wchar_t content[2]; + }; + + EscapedKey escapeCode = {}; + switch (virtualKey) { + case VK_UP: escapeCode = { EscapedKey::Letter, {'A'} }; break; + case VK_DOWN: escapeCode = { EscapedKey::Letter, {'B'} }; break; + case VK_RIGHT: escapeCode = { EscapedKey::Letter, {'C'} }; break; + case VK_LEFT: escapeCode = { EscapedKey::Letter, {'D'} }; break; + case VK_CLEAR: escapeCode = { EscapedKey::Letter, {'E'} }; break; + case VK_F1: escapeCode = { EscapedKey::Numeric, {'1', '1'} }; break; + case VK_F2: escapeCode = { EscapedKey::Numeric, {'1', '2'} }; break; + case VK_F3: escapeCode = { EscapedKey::Numeric, {'1', '3'} }; break; + case VK_F4: escapeCode = { EscapedKey::Numeric, {'1', '4'} }; break; + case VK_F5: escapeCode = { EscapedKey::Numeric, {'1', '5'} }; break; + case VK_F6: escapeCode = { EscapedKey::Numeric, {'1', '7'} }; break; + case VK_F7: escapeCode = { EscapedKey::Numeric, {'1', '8'} }; break; + case VK_F8: escapeCode = { EscapedKey::Numeric, {'1', '9'} }; break; + case VK_F9: escapeCode = { EscapedKey::Numeric, {'2', '0'} }; break; + case VK_F10: escapeCode = { EscapedKey::Numeric, {'2', '1'} }; break; + case VK_F11: escapeCode = { EscapedKey::Numeric, {'2', '3'} }; break; + case VK_F12: escapeCode = { EscapedKey::Numeric, {'2', '4'} }; break; + case VK_HOME: escapeCode = { EscapedKey::Letter, {'H'} }; break; + case VK_INSERT: escapeCode = { EscapedKey::Numeric, {'2'} }; break; + case VK_DELETE: escapeCode = { EscapedKey::Numeric, {'3'} }; break; + case VK_END: escapeCode = { EscapedKey::Letter, {'F'} }; break; + case VK_PRIOR: escapeCode = { EscapedKey::Numeric, {'5'} }; break; + case VK_NEXT: escapeCode = { EscapedKey::Numeric, {'6'} }; break; + } + if (escapeCode.kind != EscapedKey::None) { + int flags = 0; + if (keyState & SHIFT_PRESSED) { flags |= 0x1; } + if (keyState & LEFT_ALT_PRESSED) { flags |= 0x2; } + if (keyState & LEFT_CTRL_PRESSED) { flags |= 0x4; } + outch(out, L'\x1b'); + outch(out, L'['); + if (escapeCode.kind == EscapedKey::Numeric) { + for (wchar_t ch : escapeCode.content) { + if (ch != L'\0') { + outch(out, ch); + } + } + } else if (flags != 0) { + outch(out, L'1'); + } + if (flags != 0) { + outch(out, L';'); + outch(out, L'1' + flags); + } + if (escapeCode.kind == EscapedKey::Numeric) { + outch(out, L'~'); + } else { + outch(out, escapeCode.content[0]); + } + return; + } + + switch (virtualKey) { + case VK_BACK: + if (keyState & LEFT_ALT_PRESSED) { + outch(out, L'\x1b'); + } + outch(out, L'\x7f'); + return; + case VK_TAB: + if (keyState & SHIFT_PRESSED) { + outch(out, L'\x1b'); + outch(out, L'['); + outch(out, L'Z'); + return; + } + break; + } + + if (codePoint != 0) { + if (keyState & LEFT_ALT_PRESSED) { + outch(out, L'\x1b'); + } + ConsoleInput::appendCPInputRecords(out, TRUE, 0, codePoint, 0); + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h new file mode 100644 index 0000000000..63bc006b5a --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h @@ -0,0 +1,36 @@ +// Copyright (c) 2016 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_CONSOLE_INPUT_REENCODING_H +#define AGENT_CONSOLE_INPUT_REENCODING_H + +#include <windows.h> + +#include <stdint.h> + +#include <vector> + +void reencodeEscapedKeyPress( + std::vector<INPUT_RECORD> &records, + uint16_t virtualKey, + uint32_t codePoint, + uint16_t keyState); + +#endif // AGENT_CONSOLE_INPUT_REENCODING_H diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc new file mode 100644 index 0000000000..1d2bcb7685 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc @@ -0,0 +1,152 @@ +// 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. + +// +// ConsoleLine +// +// This data structure keep tracks of the previous CHAR_INFO content of an +// output line and determines when a line has changed. Detecting line changes +// is made complicated by terminal resizing. +// + +#include "ConsoleLine.h" + +#include <algorithm> + +#include "../shared/WinptyAssert.h" + +static CHAR_INFO blankChar(WORD attributes) +{ + // N.B.: As long as we write to UnicodeChar rather than AsciiChar, there + // are no padding bytes that could contain uninitialized bytes. This fact + // is important for efficient comparison. + CHAR_INFO ret; + ret.Attributes = attributes; + ret.Char.UnicodeChar = L' '; + return ret; +} + +static bool isLineBlank(const CHAR_INFO *line, int length, WORD attributes) +{ + for (int col = 0; col < length; ++col) { + if (line[col].Attributes != attributes || + line[col].Char.UnicodeChar != L' ') { + return false; + } + } + return true; +} + +static inline bool areLinesEqual( + const CHAR_INFO *line1, + const CHAR_INFO *line2, + int length) +{ + return memcmp(line1, line2, sizeof(CHAR_INFO) * length) == 0; +} + +ConsoleLine::ConsoleLine() : m_prevLength(0) +{ +} + +void ConsoleLine::reset() +{ + m_prevLength = 0; + m_prevData.clear(); +} + +// Determines whether the given line is sufficiently different from the +// previously seen line as to justify reoutputting the line. The function +// also sets the `ConsoleLine` to the given line, exactly as if `setLine` had +// been called. +bool ConsoleLine::detectChangeAndSetLine(const CHAR_INFO *const line, const int newLength) +{ + ASSERT(newLength >= 1); + ASSERT(m_prevLength <= static_cast<int>(m_prevData.size())); + + if (newLength == m_prevLength) { + bool equalLines = areLinesEqual(m_prevData.data(), line, newLength); + if (!equalLines) { + setLine(line, newLength); + } + return !equalLines; + } else { + if (m_prevLength == 0) { + setLine(line, newLength); + return true; + } + + ASSERT(m_prevLength >= 1); + const WORD prevBlank = m_prevData[m_prevLength - 1].Attributes; + const WORD newBlank = line[newLength - 1].Attributes; + + bool equalLines = false; + if (newLength < m_prevLength) { + // The line has become shorter. The lines are equal if the common + // part is equal, and if the newly truncated characters were blank. + equalLines = + areLinesEqual(m_prevData.data(), line, newLength) && + isLineBlank(m_prevData.data() + newLength, + m_prevLength - newLength, + newBlank); + } else { + // + // The line has become longer. The lines are equal if the common + // part is equal, and if both the extra characters and any + // potentially reexposed characters are blank. + // + // Two of the most relevant terminals for winpty--mintty and + // jediterm--don't (currently) erase the obscured content when a + // line is cleared, so we should anticipate its existence when + // making a terminal wider and reoutput the line. See: + // + // * https://github.com/mintty/mintty/issues/480 + // * https://github.com/JetBrains/jediterm/issues/118 + // + ASSERT(newLength > m_prevLength); + equalLines = + areLinesEqual(m_prevData.data(), line, m_prevLength) && + isLineBlank(m_prevData.data() + m_prevLength, + std::min<int>(m_prevData.size(), newLength) - m_prevLength, + prevBlank) && + isLineBlank(line + m_prevLength, + newLength - m_prevLength, + prevBlank); + } + setLine(line, newLength); + return !equalLines; + } +} + +void ConsoleLine::setLine(const CHAR_INFO *const line, const int newLength) +{ + if (static_cast<int>(m_prevData.size()) < newLength) { + m_prevData.resize(newLength); + } + memcpy(m_prevData.data(), line, sizeof(CHAR_INFO) * newLength); + m_prevLength = newLength; +} + +void ConsoleLine::blank(WORD attributes) +{ + m_prevData.resize(1); + m_prevData[0] = blankChar(attributes); + m_prevLength = 1; +} diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h new file mode 100644 index 0000000000..802c189c75 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h @@ -0,0 +1,41 @@ +// 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 CONSOLE_LINE_H +#define CONSOLE_LINE_H + +#include <windows.h> + +#include <vector> + +class ConsoleLine +{ +public: + ConsoleLine(); + void reset(); + bool detectChangeAndSetLine(const CHAR_INFO *line, int newLength); + void setLine(const CHAR_INFO *line, int newLength); + void blank(WORD attributes); +private: + int m_prevLength; + std::vector<CHAR_INFO> m_prevData; +}; + +#endif // CONSOLE_LINE_H diff --git a/src/libs/3rdparty/winpty/src/agent/Coord.h b/src/libs/3rdparty/winpty/src/agent/Coord.h new file mode 100644 index 0000000000..74c98addac --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Coord.h @@ -0,0 +1,87 @@ +// Copyright (c) 2011-2012 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 COORD_H +#define COORD_H + +#include <windows.h> + +#include <string> + +#include "../shared/winpty_snprintf.h" + +struct Coord : COORD { + Coord() + { + X = 0; + Y = 0; + } + + Coord(SHORT x, SHORT y) + { + X = x; + Y = y; + } + + Coord(COORD other) + { + *(COORD*)this = other; + } + + Coord(const Coord &other) + { + *(COORD*)this = *(const COORD*)&other; + } + + Coord &operator=(const Coord &other) + { + *(COORD*)this = *(const COORD*)&other; + return *this; + } + + bool operator==(const Coord &other) const + { + return X == other.X && Y == other.Y; + } + + bool operator!=(const Coord &other) const + { + return !(*this == other); + } + + Coord operator+(const Coord &other) const + { + return Coord(X + other.X, Y + other.Y); + } + + bool isEmpty() const + { + return X <= 0 || Y <= 0; + } + + std::string toString() const + { + char ret[32]; + winpty_snprintf(ret, "(%d,%d)", X, Y); + return std::string(ret); + } +}; + +#endif // COORD_H diff --git a/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc new file mode 100644 index 0000000000..191b2e1466 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc @@ -0,0 +1,239 @@ +// 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 <windows.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> + +#include "../shared/StringBuilder.h" +#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(StringBuilder &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 << pre; + } else if (wroteSomething && sep != '\0') { + out << sep; + } + out << f.text; + wroteSomething = true; + remaining &= ~f.value; + } + } + if (remaining != 0) { + if (!wroteSomething && pre != '\0') { + out << pre; + } else if (wroteSomething && sep != '\0') { + out << sep; + } + out << remainderName << "(0x" << hexOfInt(remaining) << ')'; + wroteSomething = true; + } + if (wroteSomething && post != '\0') { + out << post; + } +} + +template <size_t n> +static void writeFlags(StringBuilder &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) { + StringBuilder sb; + writeFlags(sb, controlKeyState, + "keyState", kControlKeyStates, '\0', '-', '-'); + return sb.str_moved(); +} + +std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer) { + const uint16_t buttons = mer.dwButtonState & 0xFFFF; + const int16_t wheel = mer.dwButtonState >> 16; + StringBuilder sb; + sb << "pos=" << mer.dwMousePosition.X << ',' + << mer.dwMousePosition.Y; + writeFlags(sb, mer.dwControlKeyState, "keyState", kControlKeyStates, ' ', ' ', '\0'); + writeFlags(sb, mer.dwEventFlags, "flags", kMouseEventFlags, ' ', ' ', '\0'); + writeFlags(sb, buttons, "buttons", kButtonStates, ' ', ' ', '\0'); + if (wheel != 0) { + sb << " wheel=" << wheel; + } + return sb.str_moved(); +} + +void debugShowInput(bool enableMouse, bool escapeInput) { + 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 restoreConsoleMode = origConsoleMode; + if (enableMouse && !(restoreConsoleMode & ENABLE_EXTENDED_FLAGS)) { + // We need to disable QuickEdit mode, because it blocks mouse events. + // If ENABLE_EXTENDED_FLAGS wasn't originally in the console mode, then + // we have no way of knowning whether QuickEdit or InsertMode are + // currently enabled. Enable them both (eventually), because they're + // sensible defaults. This case shouldn't happen typically. See + // misc/EnableExtendedFlags.txt. + restoreConsoleMode |= ENABLE_EXTENDED_FLAGS; + restoreConsoleMode |= ENABLE_QUICK_EDIT_MODE; + restoreConsoleMode |= ENABLE_INSERT_MODE; + } + DWORD newConsoleMode = restoreConsoleMode; + newConsoleMode &= ~ENABLE_PROCESSED_INPUT; + newConsoleMode &= ~ENABLE_LINE_INPUT; + newConsoleMode &= ~ENABLE_ECHO_INPUT; + newConsoleMode |= ENABLE_WINDOW_INPUT; + if (enableMouse) { + newConsoleMode |= ENABLE_MOUSE_INPUT; + newConsoleMode &= ~ENABLE_QUICK_EDIT_MODE; + } else { + newConsoleMode &= ~ENABLE_MOUSE_INPUT; + } + if (escapeInput) { + // As of this writing (2016-06-05), Microsoft has shipped two preview + // builds of Windows 10 (14316 and 14342) that include a new "Windows + // Subsystem for Linux" that runs Ubuntu in a new subsystem. Running + // bash in this subsystem requires the non-legacy console mode, and the + // console input buffer is put into a special mode where escape + // sequences are written into the console input buffer. This mode is + // enabled with the 0x200 flag, which is as-yet undocumented. + // See https://github.com/rprichard/winpty/issues/82. + newConsoleMode |= 0x200; + } + if (!SetConsoleMode(conin, newConsoleMode)) { + fprintf(stderr, "Error: could not set console mode " + "(0x%x -> 0x%x -> 0x%x)\n", + static_cast<unsigned int>(origConsoleMode), + static_cast<unsigned int>(newConsoleMode), + static_cast<unsigned int>(restoreConsoleMode)); + 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) { + StringBuilder sb; + 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<uint16_t>(ker.dwControlKeyState), + }; + sb << "key: " << (ker.bKeyDown ? "dn" : "up") + << " rpt=" << ker.wRepeatCount + << " scn=" << (ker.wVirtualScanCode ? "0x" : "") << hexOfInt(ker.wVirtualScanCode) + << ' ' << key.toString() << '\n'; + if ((ker.dwControlKeyState & + (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && + ker.wVirtualKeyCode == 'D') { + finished = true; + break; + } else if (ker.wVirtualKeyCode == 0 && + ker.wVirtualScanCode == 0 && + ker.uChar.UnicodeChar == 4) { + // Also look for a zeroed-out Ctrl-D record generated for + // ENABLE_VIRTUAL_TERMINAL_INPUT. + finished = true; + break; + } + } else if (record.EventType == MOUSE_EVENT) { + const MOUSE_EVENT_RECORD &mer = record.Event.MouseEvent; + sb << "mouse: " << mouseEventToString(mer) << '\n'; + } else if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) { + const WINDOW_BUFFER_SIZE_RECORD &wbsr = + record.Event.WindowBufferSizeEvent; + sb << "buffer-resized: dwSize=(" + << wbsr.dwSize.X << ',' + << wbsr.dwSize.Y << ")\n"; + } else if (record.EventType == MENU_EVENT) { + const MENU_EVENT_RECORD &mer = record.Event.MenuEvent; + sb << "menu-event: commandId=0x" + << hexOfInt(mer.dwCommandId) << '\n'; + } else if (record.EventType == FOCUS_EVENT) { + const FOCUS_EVENT_RECORD &fer = record.Event.FocusEvent; + sb << "focus: " << (fer.bSetFocus ? "gained" : "lost") << '\n'; + } + } + + const auto str = sb.str_moved(); + fwrite(str.data(), 1, str.size(), stdout); + fflush(stdout); + } + SetConsoleMode(conin, restoreConsoleMode); +} diff --git a/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h new file mode 100644 index 0000000000..4fa13604bd --- /dev/null +++ b/src/libs/3rdparty/winpty/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 <windows.h> + +#include <string> + +std::string controlKeyStatePrefix(DWORD controlKeyState); +std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer); +void debugShowInput(bool enableMouse, bool escapeInput); + +#endif // AGENT_DEBUG_SHOW_INPUT_H diff --git a/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc new file mode 100644 index 0000000000..5e29d98e4e --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc @@ -0,0 +1,422 @@ +// 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 "DefaultInputMap.h" + +#include <windows.h> +#include <string.h> + +#include <algorithm> + +#include "../shared/StringBuilder.h" +#include "../shared/WinptyAssert.h" +#include "InputMap.h" + +#define ESC "\x1B" +#define DIM(x) (sizeof(x) / sizeof((x)[0])) + +namespace { + +struct EscapeEncoding { + bool alt_prefix_allowed; + char prefix; + char id; + int modifiers; + InputMap::Key key; +}; + +// Modifiers. A "modifier" is an integer from 2 to 8 that conveys the status +// of Shift(1), Alt(2), and Ctrl(4). The value is constructed by OR'ing the +// appropriate value for each active modifier, then adding 1. +// +// Details: +// - kBare: expands to: ESC <prefix> <suffix> +// - kSemiMod: expands to: ESC <prefix> <numid> ; <mod> <suffix> +// - kBareMod: expands to: ESC <prefix> <mod> <suffix> +const int kBare = 0x01; +const int kSemiMod = 0x02; +const int kBareMod = 0x04; + +// Numeric escape sequences suffixes: +// - with no flag: accept: ~ +// - kSuffixCtrl: accept: ~ ^ +// - kSuffixShift: accept: ~ $ +// - kSuffixBoth: accept: ~ ^ $ @ +const int kSuffixCtrl = 0x08; +const int kSuffixShift = 0x10; +const int kSuffixBoth = kSuffixCtrl | kSuffixShift; + +static const EscapeEncoding escapeLetterEncodings[] = { + // Conventional arrow keys + // kBareMod: Ubuntu /etc/inputrc and IntelliJ/JediTerm use escapes like: ESC [ n ABCD + { true, '[', 'A', kBare | kBareMod | kSemiMod, { VK_UP, '\0', 0 } }, + { true, '[', 'B', kBare | kBareMod | kSemiMod, { VK_DOWN, '\0', 0 } }, + { true, '[', 'C', kBare | kBareMod | kSemiMod, { VK_RIGHT, '\0', 0 } }, + { true, '[', 'D', kBare | kBareMod | kSemiMod, { VK_LEFT, '\0', 0 } }, + + // putty. putty uses this sequence for Ctrl-Arrow, Shift-Arrow, and + // Ctrl-Shift-Arrow, but I can only decode to one choice, so I'm just + // leaving the modifier off altogether. + { true, 'O', 'A', kBare, { VK_UP, '\0', 0 } }, + { true, 'O', 'B', kBare, { VK_DOWN, '\0', 0 } }, + { true, 'O', 'C', kBare, { VK_RIGHT, '\0', 0 } }, + { true, 'O', 'D', kBare, { VK_LEFT, '\0', 0 } }, + + // rxvt, rxvt-unicode + // Shift-Ctrl-Arrow can't be identified. It's the same as Shift-Arrow. + { true, '[', 'a', kBare, { VK_UP, '\0', SHIFT_PRESSED } }, + { true, '[', 'b', kBare, { VK_DOWN, '\0', SHIFT_PRESSED } }, + { true, '[', 'c', kBare, { VK_RIGHT, '\0', SHIFT_PRESSED } }, + { true, '[', 'd', kBare, { VK_LEFT, '\0', SHIFT_PRESSED } }, + { true, 'O', 'a', kBare, { VK_UP, '\0', LEFT_CTRL_PRESSED } }, + { true, 'O', 'b', kBare, { VK_DOWN, '\0', LEFT_CTRL_PRESSED } }, + { true, 'O', 'c', kBare, { VK_RIGHT, '\0', LEFT_CTRL_PRESSED } }, + { true, 'O', 'd', kBare, { VK_LEFT, '\0', LEFT_CTRL_PRESSED } }, + + // Numpad 5 with NumLock off + // * xterm, mintty, and gnome-terminal use `ESC [ E`. + // * putty, TERM=cygwin, TERM=linux all use `ESC [ G` for 5 + // * putty uses `ESC O G` for Ctrl-5 and Shift-5. Omit the modifier + // as with putty's arrow keys. + // * I never saw modifiers inserted into these escapes, but I think + // it should be completely OK with the CSI escapes. + { true, '[', 'E', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } }, + { true, '[', 'G', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } }, + { true, 'O', 'G', kBare, { VK_CLEAR, '\0', 0 } }, + + // Home/End, letter version + // * gnome-terminal uses `ESC O [HF]`. I never saw it modified. + // kBareMod: IntelliJ/JediTerm uses escapes like: ESC [ n HF + { true, '[', 'H', kBare | kBareMod | kSemiMod, { VK_HOME, '\0', 0 } }, + { true, '[', 'F', kBare | kBareMod | kSemiMod, { VK_END, '\0', 0 } }, + { true, 'O', 'H', kBare, { VK_HOME, '\0', 0 } }, + { true, 'O', 'F', kBare, { VK_END, '\0', 0 } }, + + // F1-F4, letter version (xterm, VTE, konsole) + { true, '[', 'P', kSemiMod, { VK_F1, '\0', 0 } }, + { true, '[', 'Q', kSemiMod, { VK_F2, '\0', 0 } }, + { true, '[', 'R', kSemiMod, { VK_F3, '\0', 0 } }, + { true, '[', 'S', kSemiMod, { VK_F4, '\0', 0 } }, + + // GNOME VTE and Konsole have special encodings for modified F1-F4: + // * [VTE] ESC O 1 ; n [PQRS] + // * [Konsole] ESC O n [PQRS] + { false, 'O', 'P', kBare | kBareMod | kSemiMod, { VK_F1, '\0', 0 } }, + { false, 'O', 'Q', kBare | kBareMod | kSemiMod, { VK_F2, '\0', 0 } }, + { false, 'O', 'R', kBare | kBareMod | kSemiMod, { VK_F3, '\0', 0 } }, + { false, 'O', 'S', kBare | kBareMod | kSemiMod, { VK_F4, '\0', 0 } }, + + // Handle the "application numpad" escape sequences. + // + // Terminals output these codes under various circumstances: + // * rxvt-unicode: numpad, hold down SHIFT + // * rxvt: numpad, by default + // * xterm: numpad, after enabling app-mode using DECPAM (`ESC =`). xterm + // generates `ESC O <mod> <letter>` for modified numpad presses, + // necessitating kBareMod. + // * mintty: by combining Ctrl with various keys such as '1' or ','. + // Handling those keys is difficult, because mintty is generating the + // same sequence for Ctrl-1 and Ctrl-NumPadEnd -- should the virtualKey + // be '1' or VK_HOME? + + { true, 'O', 'M', kBare | kBareMod, { VK_RETURN, '\r', 0 } }, + { true, 'O', 'j', kBare | kBareMod, { VK_MULTIPLY, '*', 0 } }, + { true, 'O', 'k', kBare | kBareMod, { VK_ADD, '+', 0 } }, + { true, 'O', 'm', kBare | kBareMod, { VK_SUBTRACT, '-', 0 } }, + { true, 'O', 'n', kBare | kBareMod, { VK_DELETE, '\0', 0 } }, + { true, 'O', 'o', kBare | kBareMod, { VK_DIVIDE, '/', 0 } }, + { true, 'O', 'p', kBare | kBareMod, { VK_INSERT, '\0', 0 } }, + { true, 'O', 'q', kBare | kBareMod, { VK_END, '\0', 0 } }, + { true, 'O', 'r', kBare | kBareMod, { VK_DOWN, '\0', 0 } }, + { true, 'O', 's', kBare | kBareMod, { VK_NEXT, '\0', 0 } }, + { true, 'O', 't', kBare | kBareMod, { VK_LEFT, '\0', 0 } }, + { true, 'O', 'u', kBare | kBareMod, { VK_CLEAR, '\0', 0 } }, + { true, 'O', 'v', kBare | kBareMod, { VK_RIGHT, '\0', 0 } }, + { true, 'O', 'w', kBare | kBareMod, { VK_HOME, '\0', 0 } }, + { true, 'O', 'x', kBare | kBareMod, { VK_UP, '\0', 0 } }, + { true, 'O', 'y', kBare | kBareMod, { VK_PRIOR, '\0', 0 } }, + + { true, '[', 'M', kBare | kSemiMod, { VK_RETURN, '\r', 0 } }, + { true, '[', 'j', kBare | kSemiMod, { VK_MULTIPLY, '*', 0 } }, + { true, '[', 'k', kBare | kSemiMod, { VK_ADD, '+', 0 } }, + { true, '[', 'm', kBare | kSemiMod, { VK_SUBTRACT, '-', 0 } }, + { true, '[', 'n', kBare | kSemiMod, { VK_DELETE, '\0', 0 } }, + { true, '[', 'o', kBare | kSemiMod, { VK_DIVIDE, '/', 0 } }, + { true, '[', 'p', kBare | kSemiMod, { VK_INSERT, '\0', 0 } }, + { true, '[', 'q', kBare | kSemiMod, { VK_END, '\0', 0 } }, + { true, '[', 'r', kBare | kSemiMod, { VK_DOWN, '\0', 0 } }, + { true, '[', 's', kBare | kSemiMod, { VK_NEXT, '\0', 0 } }, + { true, '[', 't', kBare | kSemiMod, { VK_LEFT, '\0', 0 } }, + { true, '[', 'u', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } }, + { true, '[', 'v', kBare | kSemiMod, { VK_RIGHT, '\0', 0 } }, + { true, '[', 'w', kBare | kSemiMod, { VK_HOME, '\0', 0 } }, + { true, '[', 'x', kBare | kSemiMod, { VK_UP, '\0', 0 } }, + { true, '[', 'y', kBare | kSemiMod, { VK_PRIOR, '\0', 0 } }, + + { false, '[', 'Z', kBare, { VK_TAB, '\t', SHIFT_PRESSED } }, +}; + +static const EscapeEncoding escapeNumericEncodings[] = { + { true, '[', 1, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } }, + { true, '[', 2, kBare | kSemiMod | kSuffixBoth, { VK_INSERT, '\0', 0 } }, + { true, '[', 3, kBare | kSemiMod | kSuffixBoth, { VK_DELETE, '\0', 0 } }, + { true, '[', 4, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } }, + { true, '[', 5, kBare | kSemiMod | kSuffixBoth, { VK_PRIOR, '\0', 0 } }, + { true, '[', 6, kBare | kSemiMod | kSuffixBoth, { VK_NEXT, '\0', 0 } }, + { true, '[', 7, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } }, + { true, '[', 8, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } }, + { true, '[', 11, kBare | kSemiMod | kSuffixBoth, { VK_F1, '\0', 0 } }, + { true, '[', 12, kBare | kSemiMod | kSuffixBoth, { VK_F2, '\0', 0 } }, + { true, '[', 13, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', 0 } }, + { true, '[', 14, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', 0 } }, + { true, '[', 15, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', 0 } }, + { true, '[', 17, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', 0 } }, + { true, '[', 18, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', 0 } }, + { true, '[', 19, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', 0 } }, + { true, '[', 20, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', 0 } }, + { true, '[', 21, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', 0 } }, + { true, '[', 23, kBare | kSemiMod | kSuffixBoth, { VK_F11, '\0', 0 } }, + { true, '[', 24, kBare | kSemiMod | kSuffixBoth, { VK_F12, '\0', 0 } }, + { true, '[', 25, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', SHIFT_PRESSED } }, + { true, '[', 26, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', SHIFT_PRESSED } }, + { true, '[', 28, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', SHIFT_PRESSED } }, + { true, '[', 29, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', SHIFT_PRESSED } }, + { true, '[', 31, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', SHIFT_PRESSED } }, + { true, '[', 32, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', SHIFT_PRESSED } }, + { true, '[', 33, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', SHIFT_PRESSED } }, + { true, '[', 34, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', SHIFT_PRESSED } }, +}; + +const int kCsiShiftModifier = 1; +const int kCsiAltModifier = 2; +const int kCsiCtrlModifier = 4; + +static inline bool useEnhancedForVirtualKey(uint16_t vk) { + switch (vk) { + case VK_UP: + case VK_DOWN: + case VK_LEFT: + case VK_RIGHT: + case VK_INSERT: + case VK_DELETE: + case VK_HOME: + case VK_END: + case VK_PRIOR: + case VK_NEXT: + return true; + default: + return false; + } +} + +static void addSimpleEntries(InputMap &inputMap) { + struct SimpleEncoding { + const char *encoding; + InputMap::Key key; + }; + + static const SimpleEncoding simpleEncodings[] = { + // Ctrl-<letter/digit> seems to be handled OK by the default code path. + + { "\x7F", { VK_BACK, '\x08', 0, } }, + { ESC "\x7F", { VK_BACK, '\x08', LEFT_ALT_PRESSED, } }, + { "\x03", { 'C', '\x03', LEFT_CTRL_PRESSED, } }, + + // Handle special F1-F5 for TERM=linux and TERM=cygwin. + { ESC "[[A", { VK_F1, '\0', 0 } }, + { ESC "[[B", { VK_F2, '\0', 0 } }, + { ESC "[[C", { VK_F3, '\0', 0 } }, + { ESC "[[D", { VK_F4, '\0', 0 } }, + { ESC "[[E", { VK_F5, '\0', 0 } }, + + { ESC ESC "[[A", { VK_F1, '\0', LEFT_ALT_PRESSED } }, + { ESC ESC "[[B", { VK_F2, '\0', LEFT_ALT_PRESSED } }, + { ESC ESC "[[C", { VK_F3, '\0', LEFT_ALT_PRESSED } }, + { ESC ESC "[[D", { VK_F4, '\0', LEFT_ALT_PRESSED } }, + { ESC ESC "[[E", { VK_F5, '\0', LEFT_ALT_PRESSED } }, + }; + + for (size_t i = 0; i < DIM(simpleEncodings); ++i) { + auto k = simpleEncodings[i].key; + if (useEnhancedForVirtualKey(k.virtualKey)) { + k.keyState |= ENHANCED_KEY; + } + inputMap.set(simpleEncodings[i].encoding, + strlen(simpleEncodings[i].encoding), + k); + } +} + +struct ExpandContext { + InputMap &inputMap; + const EscapeEncoding &e; + char *buffer; + char *bufferEnd; +}; + +static inline void setEncoding(const ExpandContext &ctx, char *end, + uint16_t extraKeyState) { + InputMap::Key k = ctx.e.key; + k.keyState |= extraKeyState; + if (k.keyState & LEFT_CTRL_PRESSED) { + switch (k.virtualKey) { + case VK_ADD: + case VK_DIVIDE: + case VK_MULTIPLY: + case VK_SUBTRACT: + k.unicodeChar = '\0'; + break; + case VK_RETURN: + k.unicodeChar = '\n'; + break; + } + } + if (useEnhancedForVirtualKey(k.virtualKey)) { + k.keyState |= ENHANCED_KEY; + } + ctx.inputMap.set(ctx.buffer, end - ctx.buffer, k); +} + +static inline uint16_t keyStateForMod(int mod) { + int ret = 0; + if ((mod - 1) & kCsiShiftModifier) ret |= SHIFT_PRESSED; + if ((mod - 1) & kCsiAltModifier) ret |= LEFT_ALT_PRESSED; + if ((mod - 1) & kCsiCtrlModifier) ret |= LEFT_CTRL_PRESSED; + return ret; +} + +static void expandNumericEncodingSuffix(const ExpandContext &ctx, char *p, + uint16_t extraKeyState) { + ASSERT(p <= ctx.bufferEnd - 1); + { + char *q = p; + *q++ = '~'; + setEncoding(ctx, q, extraKeyState); + } + if (ctx.e.modifiers & kSuffixShift) { + char *q = p; + *q++ = '$'; + setEncoding(ctx, q, extraKeyState | SHIFT_PRESSED); + } + if (ctx.e.modifiers & kSuffixCtrl) { + char *q = p; + *q++ = '^'; + setEncoding(ctx, q, extraKeyState | LEFT_CTRL_PRESSED); + } + if (ctx.e.modifiers & (kSuffixCtrl | kSuffixShift)) { + char *q = p; + *q++ = '@'; + setEncoding(ctx, q, extraKeyState | SHIFT_PRESSED | LEFT_CTRL_PRESSED); + } +} + +template <bool is_numeric> +static inline void expandEncodingAfterAltPrefix( + const ExpandContext &ctx, char *p, uint16_t extraKeyState) { + auto appendId = [&](char *&ptr) { + const auto idstr = decOfInt(ctx.e.id); + ASSERT(ptr <= ctx.bufferEnd - idstr.size()); + std::copy(idstr.data(), idstr.data() + idstr.size(), ptr); + ptr += idstr.size(); + }; + ASSERT(p <= ctx.bufferEnd - 2); + *p++ = '\x1b'; + *p++ = ctx.e.prefix; + if (ctx.e.modifiers & kBare) { + char *q = p; + if (is_numeric) { + appendId(q); + expandNumericEncodingSuffix(ctx, q, extraKeyState); + } else { + ASSERT(q <= ctx.bufferEnd - 1); + *q++ = ctx.e.id; + setEncoding(ctx, q, extraKeyState); + } + } + if (ctx.e.modifiers & kBareMod) { + ASSERT(!is_numeric && "kBareMod is invalid with numeric sequences"); + for (int mod = 2; mod <= 8; ++mod) { + char *q = p; + ASSERT(q <= ctx.bufferEnd - 2); + *q++ = '0' + mod; + *q++ = ctx.e.id; + setEncoding(ctx, q, extraKeyState | keyStateForMod(mod)); + } + } + if (ctx.e.modifiers & kSemiMod) { + for (int mod = 2; mod <= 8; ++mod) { + char *q = p; + if (is_numeric) { + appendId(q); + ASSERT(q <= ctx.bufferEnd - 2); + *q++ = ';'; + *q++ = '0' + mod; + expandNumericEncodingSuffix( + ctx, q, extraKeyState | keyStateForMod(mod)); + } else { + ASSERT(q <= ctx.bufferEnd - 4); + *q++ = '1'; + *q++ = ';'; + *q++ = '0' + mod; + *q++ = ctx.e.id; + setEncoding(ctx, q, extraKeyState | keyStateForMod(mod)); + } + } + } +} + +template <bool is_numeric> +static inline void expandEncoding(const ExpandContext &ctx) { + if (ctx.e.alt_prefix_allowed) { + // For better or for worse, this code expands all of: + // * ESC [ <key> -- <key> + // * ESC ESC [ <key> -- Alt-<key> + // * ESC [ 1 ; 3 <key> -- Alt-<key> + // * ESC ESC [ 1 ; 3 <key> -- Alt-<key> specified twice + // I suspect no terminal actually emits the last one (i.e. specifying + // the Alt modifier using both methods), but I have seen a terminal + // that emitted a prefix ESC for Alt and a non-Alt modifier. + char *p = ctx.buffer; + ASSERT(p <= ctx.bufferEnd - 1); + *p++ = '\x1b'; + expandEncodingAfterAltPrefix<is_numeric>(ctx, p, LEFT_ALT_PRESSED); + } + expandEncodingAfterAltPrefix<is_numeric>(ctx, ctx.buffer, 0); +} + +template <bool is_numeric, size_t N> +static void addEscapes(InputMap &inputMap, const EscapeEncoding (&encodings)[N]) { + char buffer[32]; + for (size_t i = 0; i < DIM(encodings); ++i) { + ExpandContext ctx = { + inputMap, encodings[i], + buffer, buffer + sizeof(buffer) + }; + expandEncoding<is_numeric>(ctx); + } +} + +} // anonymous namespace + +void addDefaultEntriesToInputMap(InputMap &inputMap) { + addEscapes<false>(inputMap, escapeLetterEncodings); + addEscapes<true>(inputMap, escapeNumericEncodings); + addSimpleEntries(inputMap); +} diff --git a/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h new file mode 100644 index 0000000000..c4b9083678 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h @@ -0,0 +1,28 @@ +// 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 DEFAULT_INPUT_MAP_H +#define DEFAULT_INPUT_MAP_H + +class InputMap; + +void addDefaultEntriesToInputMap(InputMap &inputMap); + +#endif // DEFAULT_INPUT_MAP_H diff --git a/src/libs/3rdparty/winpty/src/agent/DsrSender.h b/src/libs/3rdparty/winpty/src/agent/DsrSender.h new file mode 100644 index 0000000000..1ec0a97d2e --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/DsrSender.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011-2012 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 DSRSENDER_H +#define DSRSENDER_H + +class DsrSender +{ +public: + virtual void sendDsr() = 0; +}; + +#endif // DSRSENDER_H diff --git a/src/libs/3rdparty/winpty/src/agent/EventLoop.cc b/src/libs/3rdparty/winpty/src/agent/EventLoop.cc new file mode 100644 index 0000000000..ba5cf18cc8 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/EventLoop.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2011-2012 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 "EventLoop.h" + +#include <algorithm> + +#include "NamedPipe.h" +#include "../shared/DebugClient.h" +#include "../shared/WinptyAssert.h" + +EventLoop::~EventLoop() { + for (NamedPipe *pipe : m_pipes) { + delete pipe; + } + m_pipes.clear(); +} + +// Enter the event loop. Runs until the I/O or timeout handler calls exit(). +void EventLoop::run() +{ + std::vector<HANDLE> waitHandles; + DWORD lastTime = GetTickCount(); + while (!m_exiting) { + bool didSomething = false; + + // Attempt to make progress with the pipes. + waitHandles.clear(); + for (size_t i = 0; i < m_pipes.size(); ++i) { + if (m_pipes[i]->serviceIo(&waitHandles)) { + onPipeIo(*m_pipes[i]); + didSomething = true; + } + } + + // Call the timeout if enough time has elapsed. + if (m_pollInterval > 0) { + int elapsed = GetTickCount() - lastTime; + if (elapsed >= m_pollInterval) { + onPollTimeout(); + lastTime = GetTickCount(); + didSomething = true; + } + } + + if (didSomething) + continue; + + // If there's nothing to do, wait. + DWORD timeout = INFINITE; + if (m_pollInterval > 0) + timeout = std::max(0, (int)(lastTime + m_pollInterval - GetTickCount())); + if (waitHandles.size() == 0) { + ASSERT(timeout != INFINITE); + if (timeout > 0) + Sleep(timeout); + } else { + DWORD result = WaitForMultipleObjects(waitHandles.size(), + waitHandles.data(), + FALSE, + timeout); + ASSERT(result != WAIT_FAILED); + } + } +} + +NamedPipe &EventLoop::createNamedPipe() +{ + NamedPipe *ret = new NamedPipe(); + m_pipes.push_back(ret); + return *ret; +} + +void EventLoop::setPollInterval(int ms) +{ + m_pollInterval = ms; +} + +void EventLoop::shutdown() +{ + m_exiting = true; +} diff --git a/src/libs/3rdparty/winpty/src/agent/EventLoop.h b/src/libs/3rdparty/winpty/src/agent/EventLoop.h new file mode 100644 index 0000000000..eddb0f6267 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/EventLoop.h @@ -0,0 +1,47 @@ +// Copyright (c) 2011-2012 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 EVENTLOOP_H +#define EVENTLOOP_H + +#include <vector> + +class NamedPipe; + +class EventLoop +{ +public: + virtual ~EventLoop(); + void run(); + +protected: + NamedPipe &createNamedPipe(); + void setPollInterval(int ms); + void shutdown(); + virtual void onPollTimeout() {} + virtual void onPipeIo(NamedPipe &namedPipe) {} + +private: + bool m_exiting = false; + std::vector<NamedPipe*> m_pipes; + int m_pollInterval = 0; +}; + +#endif // EVENTLOOP_H diff --git a/src/libs/3rdparty/winpty/src/agent/InputMap.cc b/src/libs/3rdparty/winpty/src/agent/InputMap.cc new file mode 100644 index 0000000000..b1fbfc2e30 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/InputMap.cc @@ -0,0 +1,246 @@ +// Copyright (c) 2011-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 "InputMap.h" + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "DebugShowInput.h" +#include "SimplePool.h" +#include "../shared/DebugClient.h" +#include "../shared/UnixCtrlChars.h" +#include "../shared/WinptyAssert.h" +#include "../shared/winpty_snprintf.h" + +namespace { + +static const char *getVirtualKeyString(int virtualKey) +{ + switch (virtualKey) { +#define WINPTY_GVKS_KEY(x) case VK_##x: return #x; + WINPTY_GVKS_KEY(RBUTTON) WINPTY_GVKS_KEY(F9) + WINPTY_GVKS_KEY(CANCEL) WINPTY_GVKS_KEY(F10) + WINPTY_GVKS_KEY(MBUTTON) WINPTY_GVKS_KEY(F11) + WINPTY_GVKS_KEY(XBUTTON1) WINPTY_GVKS_KEY(F12) + WINPTY_GVKS_KEY(XBUTTON2) WINPTY_GVKS_KEY(F13) + WINPTY_GVKS_KEY(BACK) WINPTY_GVKS_KEY(F14) + WINPTY_GVKS_KEY(TAB) WINPTY_GVKS_KEY(F15) + WINPTY_GVKS_KEY(CLEAR) WINPTY_GVKS_KEY(F16) + WINPTY_GVKS_KEY(RETURN) WINPTY_GVKS_KEY(F17) + WINPTY_GVKS_KEY(SHIFT) WINPTY_GVKS_KEY(F18) + WINPTY_GVKS_KEY(CONTROL) WINPTY_GVKS_KEY(F19) + WINPTY_GVKS_KEY(MENU) WINPTY_GVKS_KEY(F20) + WINPTY_GVKS_KEY(PAUSE) WINPTY_GVKS_KEY(F21) + WINPTY_GVKS_KEY(CAPITAL) WINPTY_GVKS_KEY(F22) + WINPTY_GVKS_KEY(HANGUL) WINPTY_GVKS_KEY(F23) + WINPTY_GVKS_KEY(JUNJA) WINPTY_GVKS_KEY(F24) + WINPTY_GVKS_KEY(FINAL) WINPTY_GVKS_KEY(NUMLOCK) + WINPTY_GVKS_KEY(KANJI) WINPTY_GVKS_KEY(SCROLL) + WINPTY_GVKS_KEY(ESCAPE) WINPTY_GVKS_KEY(LSHIFT) + WINPTY_GVKS_KEY(CONVERT) WINPTY_GVKS_KEY(RSHIFT) + WINPTY_GVKS_KEY(NONCONVERT) WINPTY_GVKS_KEY(LCONTROL) + WINPTY_GVKS_KEY(ACCEPT) WINPTY_GVKS_KEY(RCONTROL) + WINPTY_GVKS_KEY(MODECHANGE) WINPTY_GVKS_KEY(LMENU) + WINPTY_GVKS_KEY(SPACE) WINPTY_GVKS_KEY(RMENU) + WINPTY_GVKS_KEY(PRIOR) WINPTY_GVKS_KEY(BROWSER_BACK) + WINPTY_GVKS_KEY(NEXT) WINPTY_GVKS_KEY(BROWSER_FORWARD) + WINPTY_GVKS_KEY(END) WINPTY_GVKS_KEY(BROWSER_REFRESH) + WINPTY_GVKS_KEY(HOME) WINPTY_GVKS_KEY(BROWSER_STOP) + WINPTY_GVKS_KEY(LEFT) WINPTY_GVKS_KEY(BROWSER_SEARCH) + WINPTY_GVKS_KEY(UP) WINPTY_GVKS_KEY(BROWSER_FAVORITES) + WINPTY_GVKS_KEY(RIGHT) WINPTY_GVKS_KEY(BROWSER_HOME) + WINPTY_GVKS_KEY(DOWN) WINPTY_GVKS_KEY(VOLUME_MUTE) + WINPTY_GVKS_KEY(SELECT) WINPTY_GVKS_KEY(VOLUME_DOWN) + WINPTY_GVKS_KEY(PRINT) WINPTY_GVKS_KEY(VOLUME_UP) + WINPTY_GVKS_KEY(EXECUTE) WINPTY_GVKS_KEY(MEDIA_NEXT_TRACK) + WINPTY_GVKS_KEY(SNAPSHOT) WINPTY_GVKS_KEY(MEDIA_PREV_TRACK) + WINPTY_GVKS_KEY(INSERT) WINPTY_GVKS_KEY(MEDIA_STOP) + WINPTY_GVKS_KEY(DELETE) WINPTY_GVKS_KEY(MEDIA_PLAY_PAUSE) + WINPTY_GVKS_KEY(HELP) WINPTY_GVKS_KEY(LAUNCH_MAIL) + WINPTY_GVKS_KEY(LWIN) WINPTY_GVKS_KEY(LAUNCH_MEDIA_SELECT) + WINPTY_GVKS_KEY(RWIN) WINPTY_GVKS_KEY(LAUNCH_APP1) + WINPTY_GVKS_KEY(APPS) WINPTY_GVKS_KEY(LAUNCH_APP2) + WINPTY_GVKS_KEY(SLEEP) WINPTY_GVKS_KEY(OEM_1) + WINPTY_GVKS_KEY(NUMPAD0) WINPTY_GVKS_KEY(OEM_PLUS) + WINPTY_GVKS_KEY(NUMPAD1) WINPTY_GVKS_KEY(OEM_COMMA) + WINPTY_GVKS_KEY(NUMPAD2) WINPTY_GVKS_KEY(OEM_MINUS) + WINPTY_GVKS_KEY(NUMPAD3) WINPTY_GVKS_KEY(OEM_PERIOD) + WINPTY_GVKS_KEY(NUMPAD4) WINPTY_GVKS_KEY(OEM_2) + WINPTY_GVKS_KEY(NUMPAD5) WINPTY_GVKS_KEY(OEM_3) + WINPTY_GVKS_KEY(NUMPAD6) WINPTY_GVKS_KEY(OEM_4) + WINPTY_GVKS_KEY(NUMPAD7) WINPTY_GVKS_KEY(OEM_5) + WINPTY_GVKS_KEY(NUMPAD8) WINPTY_GVKS_KEY(OEM_6) + WINPTY_GVKS_KEY(NUMPAD9) WINPTY_GVKS_KEY(OEM_7) + WINPTY_GVKS_KEY(MULTIPLY) WINPTY_GVKS_KEY(OEM_8) + WINPTY_GVKS_KEY(ADD) WINPTY_GVKS_KEY(OEM_102) + WINPTY_GVKS_KEY(SEPARATOR) WINPTY_GVKS_KEY(PROCESSKEY) + WINPTY_GVKS_KEY(SUBTRACT) WINPTY_GVKS_KEY(PACKET) + WINPTY_GVKS_KEY(DECIMAL) WINPTY_GVKS_KEY(ATTN) + WINPTY_GVKS_KEY(DIVIDE) WINPTY_GVKS_KEY(CRSEL) + WINPTY_GVKS_KEY(F1) WINPTY_GVKS_KEY(EXSEL) + WINPTY_GVKS_KEY(F2) WINPTY_GVKS_KEY(EREOF) + WINPTY_GVKS_KEY(F3) WINPTY_GVKS_KEY(PLAY) + WINPTY_GVKS_KEY(F4) WINPTY_GVKS_KEY(ZOOM) + WINPTY_GVKS_KEY(F5) WINPTY_GVKS_KEY(NONAME) + WINPTY_GVKS_KEY(F6) WINPTY_GVKS_KEY(PA1) + WINPTY_GVKS_KEY(F7) WINPTY_GVKS_KEY(OEM_CLEAR) + WINPTY_GVKS_KEY(F8) +#undef WINPTY_GVKS_KEY + default: return NULL; + } +} + +} // anonymous namespace + +std::string InputMap::Key::toString() const { + std::string ret; + ret += controlKeyStatePrefix(keyState); + char buf[256]; + const char *vkString = getVirtualKeyString(virtualKey); + if (vkString != NULL) { + ret += vkString; + } else if ((virtualKey >= 'A' && virtualKey <= 'Z') || + (virtualKey >= '0' && virtualKey <= '9')) { + ret += static_cast<char>(virtualKey); + } else { + winpty_snprintf(buf, "%#x", virtualKey); + ret += buf; + } + if (unicodeChar >= 32 && unicodeChar <= 126) { + winpty_snprintf(buf, " ch='%c'", + static_cast<char>(unicodeChar)); + } else { + winpty_snprintf(buf, " ch=%#x", + static_cast<unsigned int>(unicodeChar)); + } + ret += buf; + return ret; +} + +void InputMap::set(const char *encoding, int encodingLen, const Key &key) { + ASSERT(encodingLen > 0); + setHelper(m_root, encoding, encodingLen, key); +} + +void InputMap::setHelper(Node &node, const char *encoding, int encodingLen, const Key &key) { + if (encodingLen == 0) { + node.key = key; + } else { + setHelper(getOrCreateChild(node, encoding[0]), encoding + 1, encodingLen - 1, key); + } +} + +InputMap::Node &InputMap::getOrCreateChild(Node &node, unsigned char ch) { + Node *ret = getChild(node, ch); + if (ret != NULL) { + return *ret; + } + if (node.childCount < Node::kTinyCount) { + // Maintain sorted order for the sake of the InputMap dumping. + int insertIndex = node.childCount; + for (int i = 0; i < node.childCount; ++i) { + if (ch < node.u.tiny.values[i]) { + insertIndex = i; + break; + } + } + for (int j = node.childCount; j > insertIndex; --j) { + node.u.tiny.values[j] = node.u.tiny.values[j - 1]; + node.u.tiny.children[j] = node.u.tiny.children[j - 1]; + } + node.u.tiny.values[insertIndex] = ch; + node.u.tiny.children[insertIndex] = ret = m_nodePool.alloc(); + ++node.childCount; + return *ret; + } + if (node.childCount == Node::kTinyCount) { + Branch *branch = m_branchPool.alloc(); + for (int i = 0; i < node.childCount; ++i) { + branch->children[node.u.tiny.values[i]] = node.u.tiny.children[i]; + } + node.u.branch = branch; + } + node.u.branch->children[ch] = ret = m_nodePool.alloc(); + ++node.childCount; + return *ret; +} + +// Find the longest matching key and node. +int InputMap::lookupKey(const char *input, int inputSize, + Key &keyOut, bool &incompleteOut) const { + keyOut = kKeyZero; + incompleteOut = false; + + const Node *node = &m_root; + InputMap::Key longestMatch = kKeyZero; + int longestMatchLen = 0; + + for (int i = 0; i < inputSize; ++i) { + unsigned char ch = input[i]; + node = getChild(*node, ch); + if (node == NULL) { + keyOut = longestMatch; + return longestMatchLen; + } else if (node->hasKey()) { + longestMatchLen = i + 1; + longestMatch = node->key; + } + } + keyOut = longestMatch; + incompleteOut = node->childCount > 0; + return longestMatchLen; +} + +void InputMap::dumpInputMap() const { + std::string encoding; + dumpInputMapHelper(m_root, encoding); +} + +void InputMap::dumpInputMapHelper( + const Node &node, std::string &encoding) const { + if (node.hasKey()) { + trace("%s -> %s", + encoding.c_str(), + node.key.toString().c_str()); + } + for (int i = 0; i < 256; ++i) { + const Node *child = getChild(node, i); + if (child != NULL) { + size_t oldSize = encoding.size(); + if (!encoding.empty()) { + encoding.push_back(' '); + } + char ctrlChar = decodeUnixCtrlChar(i); + if (ctrlChar != '\0') { + encoding.push_back('^'); + encoding.push_back(static_cast<char>(ctrlChar)); + } else if (i == ' ') { + encoding.append("' '"); + } else { + encoding.push_back(static_cast<char>(i)); + } + dumpInputMapHelper(*child, encoding); + encoding.resize(oldSize); + } + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/InputMap.h b/src/libs/3rdparty/winpty/src/agent/InputMap.h new file mode 100644 index 0000000000..9a666c7976 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/InputMap.h @@ -0,0 +1,114 @@ +// Copyright (c) 2011-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 INPUT_MAP_H +#define INPUT_MAP_H + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <string> + +#include "SimplePool.h" +#include "../shared/WinptyAssert.h" + +class InputMap { +public: + struct Key { + uint16_t virtualKey; + uint32_t unicodeChar; + uint16_t keyState; + + std::string toString() const; + }; + +private: + struct Node; + + struct Branch { + Branch() { + memset(&children, 0, sizeof(children)); + } + + Node *children[256]; + }; + + struct Node { + Node() : childCount(0) { + Key zeroKey = { 0, 0, 0 }; + key = zeroKey; + } + + Key key; + int childCount; + enum { kTinyCount = 8 }; + union { + Branch *branch; + struct { + unsigned char values[kTinyCount]; + Node *children[kTinyCount]; + } tiny; + } u; + + bool hasKey() const { + return key.virtualKey != 0 || key.unicodeChar != 0; + } + }; + +private: + SimplePool<Node, 256> m_nodePool; + SimplePool<Branch, 8> m_branchPool; + Node m_root; + +public: + void set(const char *encoding, int encodingLen, const Key &key); + int lookupKey(const char *input, int inputSize, + Key &keyOut, bool &incompleteOut) const; + void dumpInputMap() const; + +private: + Node *getChild(Node &node, unsigned char ch) { + return const_cast<Node*>(getChild(static_cast<const Node&>(node), ch)); + } + + const Node *getChild(const Node &node, unsigned char ch) const { + if (node.childCount <= Node::kTinyCount) { + for (int i = 0; i < node.childCount; ++i) { + if (node.u.tiny.values[i] == ch) { + return node.u.tiny.children[i]; + } + } + return NULL; + } else { + return node.u.branch->children[ch]; + } + } + + void setHelper(Node &node, const char *encoding, int encodingLen, const Key &key); + Node &getOrCreateChild(Node &node, unsigned char ch); + void dumpInputMapHelper(const Node &node, std::string &encoding) const; +}; + +const InputMap::Key kKeyZero = { 0, 0, 0 }; + +void dumpInputMap(InputMap &inputMap); + +#endif // INPUT_MAP_H diff --git a/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc new file mode 100644 index 0000000000..80ac640e48 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc @@ -0,0 +1,71 @@ +// 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 "LargeConsoleRead.h" + +#include <stdlib.h> + +#include "../shared/WindowsVersion.h" +#include "Scraper.h" +#include "Win32ConsoleBuffer.h" + +LargeConsoleReadBuffer::LargeConsoleReadBuffer() : + m_rect(0, 0, 0, 0), m_rectWidth(0) +{ +} + +void largeConsoleRead(LargeConsoleReadBuffer &out, + Win32ConsoleBuffer &buffer, + const SmallRect &readArea, + WORD attributesMask) { + ASSERT(readArea.Left >= 0 && + readArea.Top >= 0 && + readArea.Right >= readArea.Left && + readArea.Bottom >= readArea.Top && + readArea.width() <= MAX_CONSOLE_WIDTH); + const size_t count = readArea.width() * readArea.height(); + if (out.m_data.size() < count) { + out.m_data.resize(count); + } + out.m_rect = readArea; + out.m_rectWidth = readArea.width(); + + static const bool useLargeReads = isAtLeastWindows8(); + if (useLargeReads) { + buffer.read(readArea, out.m_data.data()); + } else { + const int maxReadLines = std::max(1, MAX_CONSOLE_WIDTH / readArea.width()); + int curLine = readArea.Top; + while (curLine <= readArea.Bottom) { + const SmallRect subReadArea( + readArea.Left, + curLine, + readArea.width(), + std::min(maxReadLines, readArea.Bottom + 1 - curLine)); + buffer.read(subReadArea, out.lineDataMut(curLine)); + curLine = subReadArea.Bottom + 1; + } + } + if (attributesMask != static_cast<WORD>(~0)) { + for (size_t i = 0; i < count; ++i) { + out.m_data[i].Attributes &= attributesMask; + } + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h new file mode 100644 index 0000000000..1bcf2c0232 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h @@ -0,0 +1,68 @@ +// 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 LARGE_CONSOLE_READ_H +#define LARGE_CONSOLE_READ_H + +#include <windows.h> +#include <stdlib.h> + +#include <vector> + +#include "SmallRect.h" +#include "../shared/DebugClient.h" +#include "../shared/WinptyAssert.h" + +class Win32ConsoleBuffer; + +class LargeConsoleReadBuffer { +public: + LargeConsoleReadBuffer(); + const SmallRect &rect() const { return m_rect; } + const CHAR_INFO *lineData(int line) const { + validateLineNumber(line); + return &m_data[(line - m_rect.Top) * m_rectWidth]; + } + +private: + CHAR_INFO *lineDataMut(int line) { + validateLineNumber(line); + return &m_data[(line - m_rect.Top) * m_rectWidth]; + } + + void validateLineNumber(int line) const { + if (line < m_rect.Top || line > m_rect.Bottom) { + trace("Fatal error: LargeConsoleReadBuffer: invalid line %d for " + "read rect %s", line, m_rect.toString().c_str()); + abort(); + } + } + + SmallRect m_rect; + int m_rectWidth; + std::vector<CHAR_INFO> m_data; + + friend void largeConsoleRead(LargeConsoleReadBuffer &out, + Win32ConsoleBuffer &buffer, + const SmallRect &readArea, + WORD attributesMask); +}; + +#endif // LARGE_CONSOLE_READ_H diff --git a/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc b/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc new file mode 100644 index 0000000000..64044e6e5d --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2011-2012 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 <string.h> + +#include <algorithm> + +#include "EventLoop.h" +#include "NamedPipe.h" +#include "../shared/DebugClient.h" +#include "../shared/StringUtil.h" +#include "../shared/WindowsSecurity.h" +#include "../shared/WinptyAssert.h" + +// Returns true if anything happens (data received, data sent, pipe error). +bool NamedPipe::serviceIo(std::vector<HANDLE> *waitHandles) +{ + bool justConnected = false; + const auto kError = ServiceResult::Error; + const auto kProgress = ServiceResult::Progress; + const auto kNoProgress = ServiceResult::NoProgress; + if (m_handle == NULL) { + return false; + } + if (m_connectEvent.get() != nullptr) { + // We're still connecting this server pipe. Check whether the pipe is + // now connected. If it isn't, add the pipe to the list of handles to + // wait on. + DWORD actual = 0; + BOOL success = + GetOverlappedResult(m_handle, &m_connectOver, &actual, FALSE); + if (!success && GetLastError() == ERROR_PIPE_CONNECTED) { + // I'm not sure this can happen, but it's easy to handle if it + // does. + success = TRUE; + } + if (!success) { + ASSERT(GetLastError() == ERROR_IO_INCOMPLETE && + "Pended ConnectNamedPipe call failed"); + waitHandles->push_back(m_connectEvent.get()); + } else { + TRACE("Server pipe [%s] connected", + utf8FromWide(m_name).c_str()); + m_connectEvent.dispose(); + startPipeWorkers(); + justConnected = true; + } + } + const auto readProgress = m_inputWorker ? m_inputWorker->service() : kNoProgress; + const auto writeProgress = m_outputWorker ? m_outputWorker->service() : kNoProgress; + if (readProgress == kError || writeProgress == kError) { + closePipe(); + return true; + } + if (m_inputWorker && m_inputWorker->getWaitEvent() != nullptr) { + waitHandles->push_back(m_inputWorker->getWaitEvent()); + } + if (m_outputWorker && m_outputWorker->getWaitEvent() != nullptr) { + waitHandles->push_back(m_outputWorker->getWaitEvent()); + } + return justConnected + || readProgress == kProgress + || writeProgress == kProgress; +} + +// manual reset, initially unset +static OwnedHandle createEvent() { + HANDLE ret = CreateEventW(nullptr, TRUE, FALSE, nullptr); + ASSERT(ret != nullptr && "CreateEventW failed"); + return OwnedHandle(ret); +} + +NamedPipe::IoWorker::IoWorker(NamedPipe &namedPipe) : + m_namedPipe(namedPipe), + m_event(createEvent()) +{ +} + +NamedPipe::ServiceResult NamedPipe::IoWorker::service() +{ + ServiceResult progress = ServiceResult::NoProgress; + if (m_pending) { + DWORD actual = 0; + BOOL ret = GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, FALSE); + if (!ret) { + if (GetLastError() == ERROR_IO_INCOMPLETE) { + // There is a pending I/O. + return progress; + } else { + // Pipe error. + return ServiceResult::Error; + } + } + ResetEvent(m_event.get()); + m_pending = false; + completeIo(actual); + m_currentIoSize = 0; + progress = ServiceResult::Progress; + } + DWORD nextSize = 0; + bool isRead = false; + while (shouldIssueIo(&nextSize, &isRead)) { + m_currentIoSize = nextSize; + DWORD actual = 0; + memset(&m_over, 0, sizeof(m_over)); + m_over.hEvent = m_event.get(); + BOOL ret = isRead + ? ReadFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over) + : WriteFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over); + if (!ret) { + if (GetLastError() == ERROR_IO_PENDING) { + // There is a pending I/O. + m_pending = true; + return progress; + } else { + // Pipe error. + return ServiceResult::Error; + } + } + ResetEvent(m_event.get()); + completeIo(actual); + m_currentIoSize = 0; + progress = ServiceResult::Progress; + } + return progress; +} + +// This function is called after CancelIo has returned. We need to block until +// the I/O operations have completed, which should happen very quickly. +// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613 +void NamedPipe::IoWorker::waitForCanceledIo() +{ + if (m_pending) { + DWORD actual = 0; + GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, TRUE); + m_pending = false; + } +} + +HANDLE NamedPipe::IoWorker::getWaitEvent() +{ + return m_pending ? m_event.get() : NULL; +} + +void NamedPipe::InputWorker::completeIo(DWORD size) +{ + m_namedPipe.m_inQueue.append(m_buffer, size); +} + +bool NamedPipe::InputWorker::shouldIssueIo(DWORD *size, bool *isRead) +{ + *isRead = true; + ASSERT(!m_namedPipe.isConnecting()); + if (m_namedPipe.isClosed()) { + return false; + } else if (m_namedPipe.m_inQueue.size() < m_namedPipe.readBufferSize()) { + *size = kIoSize; + return true; + } else { + return false; + } +} + +void NamedPipe::OutputWorker::completeIo(DWORD size) +{ + ASSERT(size == m_currentIoSize); +} + +bool NamedPipe::OutputWorker::shouldIssueIo(DWORD *size, bool *isRead) +{ + *isRead = false; + if (!m_namedPipe.m_outQueue.empty()) { + auto &out = m_namedPipe.m_outQueue; + const DWORD writeSize = std::min<size_t>(out.size(), kIoSize); + std::copy(&out[0], &out[writeSize], m_buffer); + out.erase(0, writeSize); + *size = writeSize; + return true; + } else { + return false; + } +} + +DWORD NamedPipe::OutputWorker::getPendingIoSize() +{ + return m_pending ? m_currentIoSize : 0; +} + +void NamedPipe::openServerPipe(LPCWSTR pipeName, OpenMode::t openMode, + int outBufferSize, int inBufferSize) { + ASSERT(isClosed()); + ASSERT((openMode & OpenMode::Duplex) != 0); + const DWORD winOpenMode = + ((openMode & OpenMode::Reading) ? PIPE_ACCESS_INBOUND : 0) + | ((openMode & OpenMode::Writing) ? PIPE_ACCESS_OUTBOUND : 0) + | FILE_FLAG_FIRST_PIPE_INSTANCE + | FILE_FLAG_OVERLAPPED; + const auto sd = createPipeSecurityDescriptorOwnerFullControl(); + ASSERT(sd && "error creating data pipe SECURITY_DESCRIPTOR"); + SECURITY_ATTRIBUTES sa = {}; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = sd.get(); + HANDLE handle = CreateNamedPipeW( + pipeName, + /*dwOpenMode=*/winOpenMode, + /*dwPipeMode=*/rejectRemoteClientsPipeFlag(), + /*nMaxInstances=*/1, + /*nOutBufferSize=*/outBufferSize, + /*nInBufferSize=*/inBufferSize, + /*nDefaultTimeOut=*/30000, + &sa); + TRACE("opened server pipe [%s], handle == %p", + utf8FromWide(pipeName).c_str(), handle); + ASSERT(handle != INVALID_HANDLE_VALUE && "Could not open server pipe"); + m_name = pipeName; + m_handle = handle; + m_openMode = openMode; + + // Start an asynchronous connection attempt. + m_connectEvent = createEvent(); + memset(&m_connectOver, 0, sizeof(m_connectOver)); + m_connectOver.hEvent = m_connectEvent.get(); + BOOL success = ConnectNamedPipe(m_handle, &m_connectOver); + const auto err = GetLastError(); + if (!success && err == ERROR_PIPE_CONNECTED) { + success = TRUE; + } + if (success) { + TRACE("Server pipe [%s] connected", utf8FromWide(pipeName).c_str()); + m_connectEvent.dispose(); + startPipeWorkers(); + } else if (err != ERROR_IO_PENDING) { + ASSERT(false && "ConnectNamedPipe call failed"); + } +} + +void NamedPipe::connectToServer(LPCWSTR pipeName, OpenMode::t openMode) +{ + ASSERT(isClosed()); + ASSERT((openMode & OpenMode::Duplex) != 0); + HANDLE handle = CreateFileW( + pipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED, + NULL); + TRACE("connected to [%s], handle == %p", + utf8FromWide(pipeName).c_str(), handle); + ASSERT(handle != INVALID_HANDLE_VALUE && "Could not connect to pipe"); + m_name = pipeName; + m_handle = handle; + m_openMode = openMode; + startPipeWorkers(); +} + +void NamedPipe::startPipeWorkers() +{ + if (m_openMode & OpenMode::Reading) { + m_inputWorker.reset(new InputWorker(*this)); + } + if (m_openMode & OpenMode::Writing) { + m_outputWorker.reset(new OutputWorker(*this)); + } +} + +size_t NamedPipe::bytesToSend() +{ + ASSERT(m_openMode & OpenMode::Writing); + auto ret = m_outQueue.size(); + if (m_outputWorker != NULL) { + ret += m_outputWorker->getPendingIoSize(); + } + return ret; +} + +void NamedPipe::write(const void *data, size_t size) +{ + ASSERT(m_openMode & OpenMode::Writing); + m_outQueue.append(reinterpret_cast<const char*>(data), size); +} + +void NamedPipe::write(const char *text) +{ + write(text, strlen(text)); +} + +size_t NamedPipe::readBufferSize() +{ + ASSERT(m_openMode & OpenMode::Reading); + return m_readBufferSize; +} + +void NamedPipe::setReadBufferSize(size_t size) +{ + ASSERT(m_openMode & OpenMode::Reading); + m_readBufferSize = size; +} + +size_t NamedPipe::bytesAvailable() +{ + ASSERT(m_openMode & OpenMode::Reading); + return m_inQueue.size(); +} + +size_t NamedPipe::peek(void *data, size_t size) +{ + ASSERT(m_openMode & OpenMode::Reading); + const auto out = reinterpret_cast<char*>(data); + const size_t ret = std::min(size, m_inQueue.size()); + std::copy(&m_inQueue[0], &m_inQueue[ret], out); + return ret; +} + +size_t NamedPipe::read(void *data, size_t size) +{ + size_t ret = peek(data, size); + m_inQueue.erase(0, ret); + return ret; +} + +std::string NamedPipe::readToString(size_t size) +{ + ASSERT(m_openMode & OpenMode::Reading); + size_t retSize = std::min(size, m_inQueue.size()); + std::string ret = m_inQueue.substr(0, retSize); + m_inQueue.erase(0, retSize); + return ret; +} + +std::string NamedPipe::readAllToString() +{ + ASSERT(m_openMode & OpenMode::Reading); + std::string ret = m_inQueue; + m_inQueue.clear(); + return ret; +} + +void NamedPipe::closePipe() +{ + if (m_handle == NULL) { + return; + } + CancelIo(m_handle); + if (m_connectEvent.get() != nullptr) { + DWORD actual = 0; + GetOverlappedResult(m_handle, &m_connectOver, &actual, TRUE); + m_connectEvent.dispose(); + } + if (m_inputWorker) { + m_inputWorker->waitForCanceledIo(); + m_inputWorker.reset(); + } + if (m_outputWorker) { + m_outputWorker->waitForCanceledIo(); + m_outputWorker.reset(); + } + CloseHandle(m_handle); + m_handle = NULL; +} diff --git a/src/libs/3rdparty/winpty/src/agent/NamedPipe.h b/src/libs/3rdparty/winpty/src/agent/NamedPipe.h new file mode 100644 index 0000000000..0a4d8b0c75 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/NamedPipe.h @@ -0,0 +1,125 @@ +// Copyright (c) 2011-2012 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 NAMEDPIPE_H +#define NAMEDPIPE_H + +#include <windows.h> + +#include <memory> +#include <string> +#include <vector> + +#include "../shared/OwnedHandle.h" + +class EventLoop; + +class NamedPipe +{ +private: + // The EventLoop uses these private members. + friend class EventLoop; + NamedPipe() {} + ~NamedPipe() { closePipe(); } + bool serviceIo(std::vector<HANDLE> *waitHandles); + void startPipeWorkers(); + + enum class ServiceResult { NoProgress, Error, Progress }; + +private: + class IoWorker + { + public: + IoWorker(NamedPipe &namedPipe); + virtual ~IoWorker() {} + ServiceResult service(); + void waitForCanceledIo(); + HANDLE getWaitEvent(); + protected: + NamedPipe &m_namedPipe; + bool m_pending = false; + DWORD m_currentIoSize = 0; + OwnedHandle m_event; + OVERLAPPED m_over = {}; + enum { kIoSize = 64 * 1024 }; + char m_buffer[kIoSize]; + virtual void completeIo(DWORD size) = 0; + virtual bool shouldIssueIo(DWORD *size, bool *isRead) = 0; + }; + + class InputWorker : public IoWorker + { + public: + InputWorker(NamedPipe &namedPipe) : IoWorker(namedPipe) {} + protected: + virtual void completeIo(DWORD size) override; + virtual bool shouldIssueIo(DWORD *size, bool *isRead) override; + }; + + class OutputWorker : public IoWorker + { + public: + OutputWorker(NamedPipe &namedPipe) : IoWorker(namedPipe) {} + DWORD getPendingIoSize(); + protected: + virtual void completeIo(DWORD size) override; + virtual bool shouldIssueIo(DWORD *size, bool *isRead) override; + }; + +public: + struct OpenMode { + typedef int t; + enum { None = 0, Reading = 1, Writing = 2, Duplex = 3 }; + }; + + std::wstring name() const { return m_name; } + void openServerPipe(LPCWSTR pipeName, OpenMode::t openMode, + int outBufferSize, int inBufferSize); + void connectToServer(LPCWSTR pipeName, OpenMode::t openMode); + size_t bytesToSend(); + void write(const void *data, size_t size); + void write(const char *text); + size_t readBufferSize(); + void setReadBufferSize(size_t size); + size_t bytesAvailable(); + size_t peek(void *data, size_t size); + size_t read(void *data, size_t size); + std::string readToString(size_t size); + std::string readAllToString(); + void closePipe(); + bool isClosed() { return m_handle == nullptr; } + bool isConnected() { return !isClosed() && !isConnecting(); } + bool isConnecting() { return m_connectEvent.get() != nullptr; } + +private: + // Input/output buffers + std::wstring m_name; + OVERLAPPED m_connectOver = {}; + OwnedHandle m_connectEvent; + OpenMode::t m_openMode = OpenMode::None; + size_t m_readBufferSize = 64 * 1024; + std::string m_inQueue; + std::string m_outQueue; + HANDLE m_handle = nullptr; + std::unique_ptr<InputWorker> m_inputWorker; + std::unique_ptr<OutputWorker> m_outputWorker; +}; + +#endif // NAMEDPIPE_H diff --git a/src/libs/3rdparty/winpty/src/agent/Scraper.cc b/src/libs/3rdparty/winpty/src/agent/Scraper.cc new file mode 100644 index 0000000000..21f9c67104 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Scraper.cc @@ -0,0 +1,699 @@ +// Copyright (c) 2011-2016 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 "Scraper.h" + +#include <windows.h> + +#include <stdint.h> + +#include <algorithm> +#include <utility> + +#include "../shared/WinptyAssert.h" +#include "../shared/winpty_snprintf.h" + +#include "ConsoleFont.h" +#include "Win32Console.h" +#include "Win32ConsoleBuffer.h" + +namespace { + +template <typename T> +T constrained(T min, T val, T max) { + ASSERT(min <= max); + return std::min(std::max(min, val), max); +} + +} // anonymous namespace + +Scraper::Scraper( + Win32Console &console, + Win32ConsoleBuffer &buffer, + std::unique_ptr<Terminal> terminal, + Coord initialSize) : + m_console(console), + m_terminal(std::move(terminal)), + m_ptySize(initialSize) +{ + m_consoleBuffer = &buffer; + + resetConsoleTracking(Terminal::OmitClear, buffer.windowRect().top()); + + m_bufferData.resize(BUFFER_LINE_COUNT); + + // Setup the initial screen buffer and window size. + // + // Use SetConsoleWindowInfo to shrink the console window as much as + // possible -- to a 1x1 cell at the top-left. This call always succeeds. + // Prior to the new Windows 10 console, it also actually resizes the GUI + // window to 1x1 cell. Nevertheless, even though the GUI window can + // therefore be narrower than its minimum, calling + // SetConsoleScreenBufferSize with a 1x1 size still fails. + // + // While the small font intends to support large buffers, a user could + // still hit a limit imposed by their monitor width, so cap the new window + // size to GetLargestConsoleWindowSize(). + setSmallFont(buffer.conout(), initialSize.X, m_console.isNewW10()); + buffer.moveWindow(SmallRect(0, 0, 1, 1)); + buffer.resizeBufferRange(Coord(initialSize.X, BUFFER_LINE_COUNT)); + const auto largest = GetLargestConsoleWindowSize(buffer.conout()); + buffer.moveWindow(SmallRect( + 0, 0, + std::min(initialSize.X, largest.X), + std::min(initialSize.Y, largest.Y))); + buffer.setCursorPosition(Coord(0, 0)); + + // For the sake of the color translation heuristic, set the console color + // to LtGray-on-Black. + buffer.setTextAttribute(Win32ConsoleBuffer::kDefaultAttributes); + buffer.clearAllLines(m_consoleBuffer->bufferInfo()); + + m_consoleBuffer = nullptr; +} + +Scraper::~Scraper() +{ +} + +// Whether or not the agent is frozen on entry, it will be frozen on exit. +void Scraper::resizeWindow(Win32ConsoleBuffer &buffer, + Coord newSize, + ConsoleScreenBufferInfo &finalInfoOut) +{ + m_consoleBuffer = &buffer; + m_ptySize = newSize; + syncConsoleContentAndSize(true, finalInfoOut); + m_consoleBuffer = nullptr; +} + +// This function may freeze the agent, but it will not unfreeze it. +void Scraper::scrapeBuffer(Win32ConsoleBuffer &buffer, + ConsoleScreenBufferInfo &finalInfoOut) +{ + m_consoleBuffer = &buffer; + syncConsoleContentAndSize(false, finalInfoOut); + m_consoleBuffer = nullptr; +} + +void Scraper::resetConsoleTracking( + Terminal::SendClearFlag sendClear, int64_t scrapedLineCount) +{ + for (ConsoleLine &line : m_bufferData) { + line.reset(); + } + m_syncRow = -1; + m_scrapedLineCount = scrapedLineCount; + m_scrolledCount = 0; + m_maxBufferedLine = -1; + m_dirtyWindowTop = -1; + m_dirtyLineCount = 0; + m_terminal->reset(sendClear, m_scrapedLineCount); +} + +// Detect window movement. If the window moves down (presumably as a +// result of scrolling), then assume that all screen buffer lines down to +// the bottom of the window are dirty. +void Scraper::markEntireWindowDirty(const SmallRect &windowRect) +{ + m_dirtyLineCount = std::max(m_dirtyLineCount, + windowRect.top() + windowRect.height()); +} + +// Scan the screen buffer and advance the dirty line count when we find +// non-empty lines. +void Scraper::scanForDirtyLines(const SmallRect &windowRect) +{ + const int w = m_readBuffer.rect().width(); + ASSERT(m_dirtyLineCount >= 1); + const CHAR_INFO *const prevLine = + m_readBuffer.lineData(m_dirtyLineCount - 1); + WORD prevLineAttr = prevLine[w - 1].Attributes; + const int stopLine = windowRect.top() + windowRect.height(); + + for (int line = m_dirtyLineCount; line < stopLine; ++line) { + const CHAR_INFO *lineData = m_readBuffer.lineData(line); + for (int col = 0; col < w; ++col) { + const WORD colAttr = lineData[col].Attributes; + if (lineData[col].Char.UnicodeChar != L' ' || + colAttr != prevLineAttr) { + m_dirtyLineCount = line + 1; + break; + } + } + prevLineAttr = lineData[w - 1].Attributes; + } +} + +// Clear lines in the line buffer. The `firstRow` parameter is in +// screen-buffer coordinates. +void Scraper::clearBufferLines( + const int firstRow, + const int count) +{ + ASSERT(!m_directMode); + for (int row = firstRow; row < firstRow + count; ++row) { + const int64_t bufLine = row + m_scrolledCount; + m_maxBufferedLine = std::max(m_maxBufferedLine, bufLine); + m_bufferData[bufLine % BUFFER_LINE_COUNT].blank( + Win32ConsoleBuffer::kDefaultAttributes); + } +} + +static bool cursorInWindow(const ConsoleScreenBufferInfo &info) +{ + return info.dwCursorPosition.Y >= info.srWindow.Top && + info.dwCursorPosition.Y <= info.srWindow.Bottom; +} + +void Scraper::resizeImpl(const ConsoleScreenBufferInfo &origInfo) +{ + ASSERT(m_console.frozen()); + const int cols = m_ptySize.X; + const int rows = m_ptySize.Y; + Coord finalBufferSize; + + { + // + // To accommodate Windows 10, erase all lines up to the top of the + // visible window. It's hard to tell whether this is strictly + // necessary. It ensures that the sync marker won't move downward, + // and it ensures that we won't repeat lines that have already scrolled + // up into the scrollback. + // + // It *is* possible for these blank lines to reappear in the visible + // window (e.g. if the window is made taller), but because we blanked + // the lines in the line buffer, we still don't output them again. + // + const Coord origBufferSize = origInfo.bufferSize(); + const SmallRect origWindowRect = origInfo.windowRect(); + + if (m_directMode) { + for (ConsoleLine &line : m_bufferData) { + line.reset(); + } + } else { + m_consoleBuffer->clearLines(0, origWindowRect.Top, origInfo); + clearBufferLines(0, origWindowRect.Top); + if (m_syncRow != -1) { + createSyncMarker(std::min( + m_syncRow, + BUFFER_LINE_COUNT - rows + - SYNC_MARKER_LEN + - SYNC_MARKER_MARGIN)); + } + } + + finalBufferSize = Coord( + cols, + // If there was previously no scrollback (e.g. a full-screen app + // in direct mode) and we're reducing the window height, then + // reduce the console buffer's height too. + (origWindowRect.height() == origBufferSize.Y) + ? rows + : std::max<int>(rows, origBufferSize.Y)); + + // Reset the console font size. We need to do this before shrinking + // the window, because we might need to make the font bigger to permit + // a smaller window width. Making the font smaller could expand the + // screen buffer, which would hang the conhost process in the + // Windows 10 (10240 build) if the console selection is in progress, so + // unfreeze it first. + m_console.setFrozen(false); + setSmallFont(m_consoleBuffer->conout(), cols, m_console.isNewW10()); + } + + // We try to make the font small enough so that the entire screen buffer + // fits on the monitor, but it can't be guaranteed. + const auto largest = + GetLargestConsoleWindowSize(m_consoleBuffer->conout()); + const short visibleCols = std::min<short>(cols, largest.X); + const short visibleRows = std::min<short>(rows, largest.Y); + + { + // Make the window small enough. We want the console frozen during + // this step so we don't accidentally move the window above the cursor. + m_console.setFrozen(true); + const auto info = m_consoleBuffer->bufferInfo(); + const auto &bufferSize = info.dwSize; + const int tmpWindowWidth = std::min(bufferSize.X, visibleCols); + const int tmpWindowHeight = std::min(bufferSize.Y, visibleRows); + SmallRect tmpWindowRect( + 0, + std::min<int>(bufferSize.Y - tmpWindowHeight, + info.windowRect().Top), + tmpWindowWidth, + tmpWindowHeight); + if (cursorInWindow(info)) { + tmpWindowRect = tmpWindowRect.ensureLineIncluded( + info.cursorPosition().Y); + } + m_consoleBuffer->moveWindow(tmpWindowRect); + } + + { + // Resize the buffer to the final desired size. + m_console.setFrozen(false); + m_consoleBuffer->resizeBufferRange(finalBufferSize); + } + + { + // Expand the window to its full size. + m_console.setFrozen(true); + const ConsoleScreenBufferInfo info = m_consoleBuffer->bufferInfo(); + + SmallRect finalWindowRect( + 0, + std::min<int>(info.bufferSize().Y - visibleRows, + info.windowRect().Top), + visibleCols, + visibleRows); + + // + // Once a line in the screen buffer is "dirty", it should stay visible + // in the console window, so that we continue to update its content in + // the terminal. This code is particularly (only?) necessary on + // Windows 10, where making the buffer wider can rewrap lines and move + // the console window upward. + // + if (!m_directMode && m_dirtyLineCount > finalWindowRect.Bottom + 1) { + // In theory, we avoid ensureLineIncluded, because, a massive + // amount of output could have occurred while the console was + // unfrozen, so that the *top* of the window is now below the + // dirtiest tracked line. + finalWindowRect = SmallRect( + 0, m_dirtyLineCount - visibleRows, + visibleCols, visibleRows); + } + + // Highest priority constraint: ensure that the cursor remains visible. + if (cursorInWindow(info)) { + finalWindowRect = finalWindowRect.ensureLineIncluded( + info.cursorPosition().Y); + } + + m_consoleBuffer->moveWindow(finalWindowRect); + m_dirtyWindowTop = finalWindowRect.Top; + } + + ASSERT(m_console.frozen()); +} + +void Scraper::syncConsoleContentAndSize( + bool forceResize, + ConsoleScreenBufferInfo &finalInfoOut) +{ + // We'll try to avoid freezing the console by reading large chunks (or + // all!) of the screen buffer without otherwise attempting to synchronize + // with the console application. We can only do this on Windows 10 and up + // because: + // - Prior to Windows 8, the size of a ReadConsoleOutputW call was limited + // by the ~32KB RPC buffer. + // - Prior to Windows 10, an out-of-range read region crashes the caller. + // (See misc/WindowsBugCrashReader.cc.) + // + if (!m_console.isNewW10() || forceResize) { + m_console.setFrozen(true); + } + + const ConsoleScreenBufferInfo info = m_consoleBuffer->bufferInfo(); + bool cursorVisible = true; + CONSOLE_CURSOR_INFO cursorInfo = {}; + if (!GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursorInfo)) { + trace("GetConsoleCursorInfo failed"); + } else { + cursorVisible = cursorInfo.bVisible != 0; + } + + // If an app resizes the buffer height, then we enter "direct mode", where + // we stop trying to track incremental console changes. + const bool newDirectMode = (info.bufferSize().Y != BUFFER_LINE_COUNT); + if (newDirectMode != m_directMode) { + trace("Entering %s mode", newDirectMode ? "direct" : "scrolling"); + resetConsoleTracking(Terminal::SendClear, + newDirectMode ? 0 : info.windowRect().top()); + m_directMode = newDirectMode; + + // When we switch from direct->scrolling mode, make sure the console is + // the right size. + if (!m_directMode) { + m_console.setFrozen(true); + forceResize = true; + } + } + + if (m_directMode) { + // In direct-mode, resizing the console redraws the terminal, so do it + // before scraping. + if (forceResize) { + resizeImpl(info); + } + directScrapeOutput(info, cursorVisible); + } else { + if (!m_console.frozen()) { + if (!scrollingScrapeOutput(info, cursorVisible, true)) { + m_console.setFrozen(true); + } + } + if (m_console.frozen()) { + scrollingScrapeOutput(info, cursorVisible, false); + } + // In scrolling mode, we want to scrape before resizing, because we'll + // erase everything in the console buffer up to the top of the console + // window. + if (forceResize) { + resizeImpl(info); + } + } + + finalInfoOut = forceResize ? m_consoleBuffer->bufferInfo() : info; +} + +// Try to match Windows' behavior w.r.t. to the LVB attribute flags. In some +// situations, Windows ignores the LVB flags on a character cell because of +// backwards compatibility -- apparently some programs set the flags without +// intending to enable reverse-video or underscores. +// +// [rprichard 2017-01-15] I haven't actually noticed any old programs that need +// this treatment -- the motivation for this function comes from the MSDN +// documentation for SetConsoleMode and ENABLE_LVB_GRID_WORLDWIDE. +WORD Scraper::attributesMask() +{ + const auto WINPTY_ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4u; + const auto WINPTY_ENABLE_LVB_GRID_WORLDWIDE = 0x10u; + const auto WINPTY_COMMON_LVB_REVERSE_VIDEO = 0x4000u; + const auto WINPTY_COMMON_LVB_UNDERSCORE = 0x8000u; + + const auto cp = GetConsoleOutputCP(); + const auto isCjk = (cp == 932 || cp == 936 || cp == 949 || cp == 950); + + const DWORD outputMode = [this]{ + ASSERT(this->m_consoleBuffer != nullptr); + DWORD mode = 0; + if (!GetConsoleMode(this->m_consoleBuffer->conout(), &mode)) { + mode = 0; + } + return mode; + }(); + const bool hasEnableLvbGridWorldwide = + (outputMode & WINPTY_ENABLE_LVB_GRID_WORLDWIDE) != 0; + const bool hasEnableVtProcessing = + (outputMode & WINPTY_ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0; + + // The new Windows 10 console (as of 14393) seems to respect + // COMMON_LVB_REVERSE_VIDEO even in CP437 w/o the other enabling modes, so + // try to match that behavior. + const auto isReverseSupported = + isCjk || hasEnableLvbGridWorldwide || hasEnableVtProcessing || m_console.isNewW10(); + const auto isUnderscoreSupported = + isCjk || hasEnableLvbGridWorldwide || hasEnableVtProcessing; + + WORD mask = ~0; + if (!isReverseSupported) { mask &= ~WINPTY_COMMON_LVB_REVERSE_VIDEO; } + if (!isUnderscoreSupported) { mask &= ~WINPTY_COMMON_LVB_UNDERSCORE; } + return mask; +} + +void Scraper::directScrapeOutput(const ConsoleScreenBufferInfo &info, + bool consoleCursorVisible) +{ + const SmallRect windowRect = info.windowRect(); + + const SmallRect scrapeRect( + windowRect.left(), windowRect.top(), + std::min<SHORT>(std::min(windowRect.width(), m_ptySize.X), + MAX_CONSOLE_WIDTH), + std::min<SHORT>(std::min(windowRect.height(), m_ptySize.Y), + BUFFER_LINE_COUNT)); + const int w = scrapeRect.width(); + const int h = scrapeRect.height(); + + const Coord cursor = info.cursorPosition(); + const bool showTerminalCursor = + consoleCursorVisible && scrapeRect.contains(cursor); + const int cursorColumn = !showTerminalCursor ? -1 : cursor.X - scrapeRect.Left; + const int cursorLine = !showTerminalCursor ? -1 : cursor.Y - scrapeRect.Top; + + if (!showTerminalCursor) { + m_terminal->hideTerminalCursor(); + } + + largeConsoleRead(m_readBuffer, *m_consoleBuffer, scrapeRect, attributesMask()); + + for (int line = 0; line < h; ++line) { + const CHAR_INFO *const curLine = + m_readBuffer.lineData(scrapeRect.top() + line); + ConsoleLine &bufLine = m_bufferData[line]; + if (bufLine.detectChangeAndSetLine(curLine, w)) { + const int lineCursorColumn = + line == cursorLine ? cursorColumn : -1; + m_terminal->sendLine(line, curLine, w, lineCursorColumn); + } + } + + if (showTerminalCursor) { + m_terminal->showTerminalCursor(cursorColumn, cursorLine); + } +} + +bool Scraper::scrollingScrapeOutput(const ConsoleScreenBufferInfo &info, + bool consoleCursorVisible, + bool tentative) +{ + const Coord cursor = info.cursorPosition(); + const SmallRect windowRect = info.windowRect(); + + if (m_syncRow != -1) { + // If a synchronizing marker was placed into the history, look for it + // and adjust the scroll count. + const int markerRow = findSyncMarker(); + if (markerRow == -1) { + if (tentative) { + // I *think* it's possible to keep going, but it's simple to + // bail out. + return false; + } + // Something has happened. Reset the terminal. + trace("Sync marker has disappeared -- resetting the terminal" + " (m_syncCounter=%u)", + m_syncCounter); + resetConsoleTracking(Terminal::SendClear, windowRect.top()); + } else if (markerRow != m_syncRow) { + ASSERT(markerRow < m_syncRow); + m_scrolledCount += (m_syncRow - markerRow); + m_syncRow = markerRow; + // If the buffer has scrolled, then the entire window is dirty. + markEntireWindowDirty(windowRect); + } + } + + // Creating a new sync row requires clearing part of the console buffer, so + // avoid doing it if there's already a sync row that's good enough. + const int newSyncRow = + static_cast<int>(windowRect.top()) - SYNC_MARKER_LEN - SYNC_MARKER_MARGIN; + const bool shouldCreateSyncRow = + newSyncRow >= m_syncRow + SYNC_MARKER_LEN + SYNC_MARKER_MARGIN; + if (tentative && shouldCreateSyncRow) { + // It's difficult even in principle to put down a new marker if the + // console can scroll an arbitrarily amount while we're writing. + return false; + } + + // Update the dirty line count: + // - If the window has moved, the entire window is dirty. + // - Everything up to the cursor is dirty. + // - All lines above the window are dirty. + // - Any non-blank lines are dirty. + if (m_dirtyWindowTop != -1) { + if (windowRect.top() > m_dirtyWindowTop) { + // The window has moved down, presumably as a result of scrolling. + markEntireWindowDirty(windowRect); + } else if (windowRect.top() < m_dirtyWindowTop) { + if (tentative) { + // I *think* it's possible to keep going, but it's simple to + // bail out. + return false; + } + // The window has moved upward. This is generally not expected to + // happen, but the CMD/PowerShell CLS command will move the window + // to the top as part of clearing everything else in the console. + trace("Window moved upward -- resetting the terminal" + " (m_syncCounter=%u)", + m_syncCounter); + resetConsoleTracking(Terminal::SendClear, windowRect.top()); + } + } + m_dirtyWindowTop = windowRect.top(); + m_dirtyLineCount = std::max(m_dirtyLineCount, cursor.Y + 1); + m_dirtyLineCount = std::max(m_dirtyLineCount, (int)windowRect.top()); + + // There will be at least one dirty line, because there is a cursor. + ASSERT(m_dirtyLineCount >= 1); + + // The first line to scrape, in virtual line coordinates. + const int64_t firstVirtLine = std::min(m_scrapedLineCount, + windowRect.top() + m_scrolledCount); + + // Read all the data we will need from the console. Start reading with the + // first line to scrape, but adjust the the read area upward to account for + // scanForDirtyLines' need to read the previous attribute. Read to the + // bottom of the window. (It's not clear to me whether the + // m_dirtyLineCount adjustment here is strictly necessary. It isn't + // necessary so long as the cursor is inside the current window.) + const int firstReadLine = std::min<int>(firstVirtLine - m_scrolledCount, + m_dirtyLineCount - 1); + const int stopReadLine = std::max(windowRect.top() + windowRect.height(), + m_dirtyLineCount); + ASSERT(firstReadLine >= 0 && stopReadLine > firstReadLine); + largeConsoleRead(m_readBuffer, + *m_consoleBuffer, + SmallRect(0, firstReadLine, + std::min<SHORT>(info.bufferSize().X, + MAX_CONSOLE_WIDTH), + stopReadLine - firstReadLine), + attributesMask()); + + // If we're scraping the buffer without freezing it, we have to query the + // buffer position data separately from the buffer content, so the two + // could easily be out-of-sync. If they *are* out-of-sync, abort the + // scrape operation and restart it frozen. (We may have updated the + // dirty-line high-water-mark, but that should be OK.) + if (tentative) { + const auto infoCheck = m_consoleBuffer->bufferInfo(); + if (info.bufferSize() != infoCheck.bufferSize() || + info.windowRect() != infoCheck.windowRect() || + info.cursorPosition() != infoCheck.cursorPosition()) { + return false; + } + if (m_syncRow != -1 && m_syncRow != findSyncMarker()) { + return false; + } + } + + if (shouldCreateSyncRow) { + ASSERT(!tentative); + createSyncMarker(newSyncRow); + } + + // At this point, we're finished interacting (reading or writing) the + // console, and we just need to convert our collected data into terminal + // output. + + scanForDirtyLines(windowRect); + + // Note that it's possible for all the lines on the current window to + // be non-dirty. + + // The line to stop scraping at, in virtual line coordinates. + const int64_t stopVirtLine = + std::min(m_dirtyLineCount, windowRect.top() + windowRect.height()) + + m_scrolledCount; + + const bool showTerminalCursor = + consoleCursorVisible && windowRect.contains(cursor); + const int64_t cursorLine = !showTerminalCursor ? -1 : cursor.Y + m_scrolledCount; + const int cursorColumn = !showTerminalCursor ? -1 : cursor.X; + + if (!showTerminalCursor) { + m_terminal->hideTerminalCursor(); + } + + bool sawModifiedLine = false; + + const int w = m_readBuffer.rect().width(); + for (int64_t line = firstVirtLine; line < stopVirtLine; ++line) { + const CHAR_INFO *curLine = + m_readBuffer.lineData(line - m_scrolledCount); + ConsoleLine &bufLine = m_bufferData[line % BUFFER_LINE_COUNT]; + if (line > m_maxBufferedLine) { + m_maxBufferedLine = line; + sawModifiedLine = true; + } + if (sawModifiedLine) { + bufLine.setLine(curLine, w); + } else { + sawModifiedLine = bufLine.detectChangeAndSetLine(curLine, w); + } + if (sawModifiedLine) { + const int lineCursorColumn = + line == cursorLine ? cursorColumn : -1; + m_terminal->sendLine(line, curLine, w, lineCursorColumn); + } + } + + m_scrapedLineCount = windowRect.top() + m_scrolledCount; + + if (showTerminalCursor) { + m_terminal->showTerminalCursor(cursorColumn, cursorLine); + } + + return true; +} + +void Scraper::syncMarkerText(CHAR_INFO (&output)[SYNC_MARKER_LEN]) +{ + // XXX: The marker text generated here could easily collide with ordinary + // console output. Does it make sense to try to avoid the collision? + char str[SYNC_MARKER_LEN + 1]; + winpty_snprintf(str, "S*Y*N*C*%08x", m_syncCounter); + for (int i = 0; i < SYNC_MARKER_LEN; ++i) { + output[i].Char.UnicodeChar = str[i]; + output[i].Attributes = 7; + } +} + +int Scraper::findSyncMarker() +{ + ASSERT(m_syncRow >= 0); + CHAR_INFO marker[SYNC_MARKER_LEN]; + CHAR_INFO column[BUFFER_LINE_COUNT]; + syncMarkerText(marker); + SmallRect rect(0, 0, 1, m_syncRow + SYNC_MARKER_LEN); + m_consoleBuffer->read(rect, column); + int i; + for (i = m_syncRow; i >= 0; --i) { + int j; + for (j = 0; j < SYNC_MARKER_LEN; ++j) { + if (column[i + j].Char.UnicodeChar != marker[j].Char.UnicodeChar) + break; + } + if (j == SYNC_MARKER_LEN) + return i; + } + return -1; +} + +void Scraper::createSyncMarker(int row) +{ + ASSERT(row >= 1); + + // Clear the lines around the marker to ensure that Windows 10's rewrapping + // does not affect the marker. + m_consoleBuffer->clearLines(row - 1, SYNC_MARKER_LEN + 1, + m_consoleBuffer->bufferInfo()); + + // Write a new marker. + m_syncCounter++; + CHAR_INFO marker[SYNC_MARKER_LEN]; + syncMarkerText(marker); + m_syncRow = row; + SmallRect markerRect(0, m_syncRow, 1, SYNC_MARKER_LEN); + m_consoleBuffer->write(markerRect, marker); +} diff --git a/src/libs/3rdparty/winpty/src/agent/Scraper.h b/src/libs/3rdparty/winpty/src/agent/Scraper.h new file mode 100644 index 0000000000..9c10d80aed --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Scraper.h @@ -0,0 +1,103 @@ +// Copyright (c) 2011-2016 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_SCRAPER_H +#define AGENT_SCRAPER_H + +#include <windows.h> + +#include <stdint.h> + +#include <memory> +#include <vector> + +#include "ConsoleLine.h" +#include "Coord.h" +#include "LargeConsoleRead.h" +#include "SmallRect.h" +#include "Terminal.h" + +class ConsoleScreenBufferInfo; +class Win32Console; +class Win32ConsoleBuffer; + +// We must be able to issue a single ReadConsoleOutputW call of +// MAX_CONSOLE_WIDTH characters, and a single read of approximately several +// hundred fewer characters than BUFFER_LINE_COUNT. +const int BUFFER_LINE_COUNT = 3000; +const int MAX_CONSOLE_WIDTH = 2500; +const int MAX_CONSOLE_HEIGHT = 2000; +const int SYNC_MARKER_LEN = 16; +const int SYNC_MARKER_MARGIN = 200; + +class Scraper { +public: + Scraper( + Win32Console &console, + Win32ConsoleBuffer &buffer, + std::unique_ptr<Terminal> terminal, + Coord initialSize); + ~Scraper(); + void resizeWindow(Win32ConsoleBuffer &buffer, + Coord newSize, + ConsoleScreenBufferInfo &finalInfoOut); + void scrapeBuffer(Win32ConsoleBuffer &buffer, + ConsoleScreenBufferInfo &finalInfoOut); + Terminal &terminal() { return *m_terminal; } + +private: + void resetConsoleTracking( + Terminal::SendClearFlag sendClear, int64_t scrapedLineCount); + void markEntireWindowDirty(const SmallRect &windowRect); + void scanForDirtyLines(const SmallRect &windowRect); + void clearBufferLines(int firstRow, int count); + void resizeImpl(const ConsoleScreenBufferInfo &origInfo); + void syncConsoleContentAndSize(bool forceResize, + ConsoleScreenBufferInfo &finalInfoOut); + WORD attributesMask(); + void directScrapeOutput(const ConsoleScreenBufferInfo &info, + bool consoleCursorVisible); + bool scrollingScrapeOutput(const ConsoleScreenBufferInfo &info, + bool consoleCursorVisible, + bool tentative); + void syncMarkerText(CHAR_INFO (&output)[SYNC_MARKER_LEN]); + int findSyncMarker(); + void createSyncMarker(int row); + +private: + Win32Console &m_console; + Win32ConsoleBuffer *m_consoleBuffer = nullptr; + std::unique_ptr<Terminal> m_terminal; + + int m_syncRow = -1; + unsigned int m_syncCounter = 0; + + bool m_directMode = false; + Coord m_ptySize; + int64_t m_scrapedLineCount = 0; + int64_t m_scrolledCount = 0; + int64_t m_maxBufferedLine = -1; + LargeConsoleReadBuffer m_readBuffer; + std::vector<ConsoleLine> m_bufferData; + int m_dirtyWindowTop = -1; + int m_dirtyLineCount = 0; +}; + +#endif // AGENT_SCRAPER_H diff --git a/src/libs/3rdparty/winpty/src/agent/SimplePool.h b/src/libs/3rdparty/winpty/src/agent/SimplePool.h new file mode 100644 index 0000000000..41ff94a90d --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/SimplePool.h @@ -0,0 +1,75 @@ +// 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 SIMPLE_POOL_H +#define SIMPLE_POOL_H + +#include <stdlib.h> + +#include <vector> + +#include "../shared/WinptyAssert.h" + +template <typename T, size_t chunkSize> +class SimplePool { +public: + ~SimplePool(); + T *alloc(); + void clear(); +private: + struct Chunk { + size_t count; + T *data; + }; + std::vector<Chunk> m_chunks; +}; + +template <typename T, size_t chunkSize> +SimplePool<T, chunkSize>::~SimplePool() { + clear(); +} + +template <typename T, size_t chunkSize> +void SimplePool<T, chunkSize>::clear() { + for (size_t ci = 0; ci < m_chunks.size(); ++ci) { + Chunk &chunk = m_chunks[ci]; + for (size_t ti = 0; ti < chunk.count; ++ti) { + chunk.data[ti].~T(); + } + free(chunk.data); + } + m_chunks.clear(); +} + +template <typename T, size_t chunkSize> +T *SimplePool<T, chunkSize>::alloc() { + if (m_chunks.empty() || m_chunks.back().count == chunkSize) { + T *newData = reinterpret_cast<T*>(malloc(sizeof(T) * chunkSize)); + ASSERT(newData != NULL); + Chunk newChunk = { 0, newData }; + m_chunks.push_back(newChunk); + } + Chunk &chunk = m_chunks.back(); + T *ret = &chunk.data[chunk.count++]; + new (ret) T(); + return ret; +} + +#endif // SIMPLE_POOL_H diff --git a/src/libs/3rdparty/winpty/src/agent/SmallRect.h b/src/libs/3rdparty/winpty/src/agent/SmallRect.h new file mode 100644 index 0000000000..bad0b88683 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/SmallRect.h @@ -0,0 +1,143 @@ +// Copyright (c) 2011-2012 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 SMALLRECT_H +#define SMALLRECT_H + +#include <windows.h> + +#include <algorithm> +#include <string> + +#include "../shared/winpty_snprintf.h" +#include "Coord.h" + +struct SmallRect : SMALL_RECT +{ + SmallRect() + { + Left = Right = Top = Bottom = 0; + } + + SmallRect(SHORT x, SHORT y, SHORT width, SHORT height) + { + Left = x; + Top = y; + Right = x + width - 1; + Bottom = y + height - 1; + } + + SmallRect(const COORD &topLeft, const COORD &size) + { + Left = topLeft.X; + Top = topLeft.Y; + Right = Left + size.X - 1; + Bottom = Top + size.Y - 1; + } + + SmallRect(const SMALL_RECT &other) + { + *(SMALL_RECT*)this = other; + } + + SmallRect(const SmallRect &other) + { + *(SMALL_RECT*)this = *(const SMALL_RECT*)&other; + } + + SmallRect &operator=(const SmallRect &other) + { + *(SMALL_RECT*)this = *(const SMALL_RECT*)&other; + return *this; + } + + bool contains(const SmallRect &other) const + { + return other.Left >= Left && + other.Right <= Right && + other.Top >= Top && + other.Bottom <= Bottom; + } + + bool contains(const Coord &other) const + { + return other.X >= Left && + other.X <= Right && + other.Y >= Top && + other.Y <= Bottom; + } + + SmallRect intersected(const SmallRect &other) const + { + int x1 = std::max(Left, other.Left); + int x2 = std::min(Right, other.Right); + int y1 = std::max(Top, other.Top); + int y2 = std::min(Bottom, other.Bottom); + return SmallRect(x1, + y1, + std::max(0, x2 - x1 + 1), + std::max(0, y2 - y1 + 1)); + } + + SmallRect ensureLineIncluded(SHORT line) const + { + const SHORT h = height(); + if (line < Top) { + return SmallRect(Left, line, width(), h); + } else if (line > Bottom) { + return SmallRect(Left, line - h + 1, width(), h); + } else { + return *this; + } + } + + SHORT top() const { return Top; } + SHORT left() const { return Left; } + SHORT width() const { return Right - Left + 1; } + SHORT height() const { return Bottom - Top + 1; } + void setTop(SHORT top) { Top = top; } + void setLeft(SHORT left) { Left = left; } + void setWidth(SHORT width) { Right = Left + width - 1; } + void setHeight(SHORT height) { Bottom = Top + height - 1; } + Coord size() const { return Coord(width(), height()); } + + bool operator==(const SmallRect &other) const + { + return Left == other.Left && + Right == other.Right && + Top == other.Top && + Bottom == other.Bottom; + } + + bool operator!=(const SmallRect &other) const + { + return !(*this == other); + } + + std::string toString() const + { + char ret[64]; + winpty_snprintf(ret, "(x=%d,y=%d,w=%d,h=%d)", + Left, Top, width(), height()); + return std::string(ret); + } +}; + +#endif // SMALLRECT_H diff --git a/src/libs/3rdparty/winpty/src/agent/Terminal.cc b/src/libs/3rdparty/winpty/src/agent/Terminal.cc new file mode 100644 index 0000000000..afa0a36260 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Terminal.cc @@ -0,0 +1,535 @@ +// Copyright (c) 2011-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 "Terminal.h" + +#include <windows.h> +#include <stdio.h> +#include <string.h> + +#include <string> + +#include "NamedPipe.h" +#include "UnicodeEncoding.h" +#include "../shared/DebugClient.h" +#include "../shared/WinptyAssert.h" +#include "../shared/winpty_snprintf.h" + +#define CSI "\x1b[" + +// Work around the old MinGW, which lacks COMMON_LVB_LEADING_BYTE and +// COMMON_LVB_TRAILING_BYTE. +const int WINPTY_COMMON_LVB_LEADING_BYTE = 0x100; +const int WINPTY_COMMON_LVB_TRAILING_BYTE = 0x200; +const int WINPTY_COMMON_LVB_REVERSE_VIDEO = 0x4000; +const int WINPTY_COMMON_LVB_UNDERSCORE = 0x8000; + +const int COLOR_ATTRIBUTE_MASK = + FOREGROUND_BLUE | + FOREGROUND_GREEN | + FOREGROUND_RED | + FOREGROUND_INTENSITY | + BACKGROUND_BLUE | + BACKGROUND_GREEN | + BACKGROUND_RED | + BACKGROUND_INTENSITY | + WINPTY_COMMON_LVB_REVERSE_VIDEO | + WINPTY_COMMON_LVB_UNDERSCORE; + +const int FLAG_RED = 1; +const int FLAG_GREEN = 2; +const int FLAG_BLUE = 4; +const int FLAG_BRIGHT = 8; + +const int BLACK = 0; +const int DKGRAY = BLACK | FLAG_BRIGHT; +const int LTGRAY = FLAG_RED | FLAG_GREEN | FLAG_BLUE; +const int WHITE = LTGRAY | FLAG_BRIGHT; + +// SGR parameters (Select Graphic Rendition) +const int SGR_FORE = 30; +const int SGR_FORE_HI = 90; +const int SGR_BACK = 40; +const int SGR_BACK_HI = 100; + +namespace { + +static void outUInt(std::string &out, unsigned int n) +{ + char buf[32]; + char *pbuf = &buf[32]; + *(--pbuf) = '\0'; + do { + *(--pbuf) = '0' + n % 10; + n /= 10; + } while (n != 0); + out.append(pbuf); +} + +static void outputSetColorSgrParams(std::string &out, bool isFore, int color) +{ + out.push_back(';'); + const int sgrBase = isFore ? SGR_FORE : SGR_BACK; + if (color & FLAG_BRIGHT) { + // Some terminals don't support the 9X/10X "intensive" color parameters + // (e.g. the Eclipse TM terminal as of this writing). Those terminals + // will quietly ignore a 9X/10X code, and the other terminals will + // ignore a 3X/4X code if it's followed by a 9X/10X code. Therefore, + // output a 3X/4X code as a fallback, then override it. + const int colorBase = color & ~FLAG_BRIGHT; + outUInt(out, sgrBase + colorBase); + out.push_back(';'); + outUInt(out, sgrBase + (SGR_FORE_HI - SGR_FORE) + colorBase); + } else { + outUInt(out, sgrBase + color); + } +} + +static void outputSetColor(std::string &out, int color) +{ + int fore = 0; + int back = 0; + if (color & FOREGROUND_RED) fore |= FLAG_RED; + if (color & FOREGROUND_GREEN) fore |= FLAG_GREEN; + if (color & FOREGROUND_BLUE) fore |= FLAG_BLUE; + if (color & FOREGROUND_INTENSITY) fore |= FLAG_BRIGHT; + if (color & BACKGROUND_RED) back |= FLAG_RED; + if (color & BACKGROUND_GREEN) back |= FLAG_GREEN; + if (color & BACKGROUND_BLUE) back |= FLAG_BLUE; + if (color & BACKGROUND_INTENSITY) back |= FLAG_BRIGHT; + + if (color & WINPTY_COMMON_LVB_REVERSE_VIDEO) { + // n.b.: The COMMON_LVB_REVERSE_VIDEO flag also swaps + // FOREGROUND_INTENSITY and BACKGROUND_INTENSITY. Tested on + // Windows 10 v14393. + std::swap(fore, back); + } + + // Translate the fore/back colors into terminal escape codes using + // a heuristic that works OK with common white-on-black or + // black-on-white color schemes. We don't know which color scheme + // the terminal is using. It is ugly to force white-on-black text + // on a black-on-white terminal, and it's even ugly to force the + // matching scheme. It's probably relevant that the default + // fore/back terminal colors frequently do not match any of the 16 + // palette colors. + + // Typical default terminal color schemes (according to palette, + // when possible): + // - mintty: LtGray-on-Black(A) + // - putty: LtGray-on-Black(A) + // - xterm: LtGray-on-Black(A) + // - Konsole: LtGray-on-Black(A) + // - JediTerm/JetBrains: Black-on-White(B) + // - rxvt: Black-on-White(B) + + // If the background is the default color (black), then it will + // map to Black(A) or White(B). If we translate White to White, + // then a Black background and a White background in the console + // are both White with (B). Therefore, we should translate White + // using SGR 7 (Invert). The typical finished mapping table for + // background grayscale colors is: + // + // (A) White => LtGray(fore) + // (A) Black => Black(back) + // (A) LtGray => LtGray + // (A) DkGray => DkGray + // + // (B) White => Black(fore) + // (B) Black => White(back) + // (B) LtGray => LtGray + // (B) DkGray => DkGray + // + + out.append(CSI "0"); + if (back == BLACK) { + if (fore == LTGRAY) { + // The "default" foreground color. Use the terminal's + // default colors. + } else if (fore == WHITE) { + // Sending the literal color white would behave poorly if + // the terminal were black-on-white. Sending Bold is not + // guaranteed to alter the color, but it will make the text + // visually distinct, so do that instead. + out.append(";1"); + } else if (fore == DKGRAY) { + // Set the foreground color to DkGray(90) with a fallback + // of LtGray(37) for terminals that don't handle the 9X SGR + // parameters (e.g. Eclipse's TM Terminal as of this + // writing). + out.append(";37;90"); + } else { + outputSetColorSgrParams(out, true, fore); + } + } else if (back == WHITE) { + // Set the background color using Invert on the default + // foreground color, and set the foreground color by setting a + // background color. + + // Use the terminal's inverted colors. + out.append(";7"); + if (fore == LTGRAY || fore == BLACK) { + // We're likely mapping Console White to terminal LtGray or + // Black. If they are the Console foreground color, then + // don't set a terminal foreground color to avoid creating + // invisible text. + } else { + outputSetColorSgrParams(out, false, fore); + } + } else { + // Set the foreground and background to match exactly that in + // the Windows console. + outputSetColorSgrParams(out, true, fore); + outputSetColorSgrParams(out, false, back); + } + if (fore == back) { + // The foreground and background colors are exactly equal, so + // attempt to hide the text using the Conceal SGR parameter, + // which some terminals support. + out.append(";8"); + } + if (color & WINPTY_COMMON_LVB_UNDERSCORE) { + out.append(";4"); + } + out.push_back('m'); +} + +static inline unsigned int fixSpecialCharacters(unsigned int ch) +{ + if (ch <= 0x1b) { + switch (ch) { + // The Windows Console has a popup window (e.g. that appears with + // F7) that is sometimes bordered with box-drawing characters. + // With the Japanese and Korean system locales (CP932 and CP949), + // the UnicodeChar values for the box-drawing characters are 1 + // through 6. Detect this and map the values to the correct + // Unicode values. + // + // N.B. In the English locale, the UnicodeChar values are correct, + // and they identify single-line characters rather than + // double-line. In the Chinese Simplified and Traditional locales, + // the popups use ASCII characters instead. + case 1: return 0x2554; // BOX DRAWINGS DOUBLE DOWN AND RIGHT + case 2: return 0x2557; // BOX DRAWINGS DOUBLE DOWN AND LEFT + case 3: return 0x255A; // BOX DRAWINGS DOUBLE UP AND RIGHT + case 4: return 0x255D; // BOX DRAWINGS DOUBLE UP AND LEFT + case 5: return 0x2551; // BOX DRAWINGS DOUBLE VERTICAL + case 6: return 0x2550; // BOX DRAWINGS DOUBLE HORIZONTAL + + // Convert an escape character to some other character. This + // conversion only applies to console cells containing an escape + // character. In newer versions of Windows 10 (e.g. 10.0.10586), + // the non-legacy console recognizes escape sequences in + // WriteConsole and interprets them without writing them to the + // cells of the screen buffer. In that case, the conversion here + // does not apply. + case 0x1b: return '?'; + } + } + return ch; +} + +static inline bool isFullWidthCharacter(const CHAR_INFO *data, int width) +{ + if (width < 2) { + return false; + } + return + (data[0].Attributes & WINPTY_COMMON_LVB_LEADING_BYTE) && + (data[1].Attributes & WINPTY_COMMON_LVB_TRAILING_BYTE) && + data[0].Char.UnicodeChar == data[1].Char.UnicodeChar; +} + +// Scan to find a single Unicode Scalar Value. Full-width characters occupy +// two console cells, and this code also tries to handle UTF-16 surrogate +// pairs. +// +// Windows expands at least some wide characters outside the Basic +// Multilingual Plane into four cells, such as U+20000: +// 1. 0xD840, attr=0x107 +// 2. 0xD840, attr=0x207 +// 3. 0xDC00, attr=0x107 +// 4. 0xDC00, attr=0x207 +// Even in the Traditional Chinese locale on Windows 10, this text is rendered +// as two boxes, but if those boxes are copied-and-pasted, the character is +// copied correctly. +static inline void scanUnicodeScalarValue( + const CHAR_INFO *data, int width, + int &outCellCount, unsigned int &outCharValue) +{ + ASSERT(width >= 1); + + const int w1 = isFullWidthCharacter(data, width) ? 2 : 1; + const wchar_t c1 = data[0].Char.UnicodeChar; + + if ((c1 & 0xF800) == 0xD800) { + // The first cell is either a leading or trailing surrogate pair. + if ((c1 & 0xFC00) != 0xD800 || + width <= w1 || + ((data[w1].Char.UnicodeChar & 0xFC00) != 0xDC00)) { + // Invalid surrogate pair + outCellCount = w1; + outCharValue = '?'; + } else { + // Valid surrogate pair + outCellCount = w1 + (isFullWidthCharacter(&data[w1], width - w1) ? 2 : 1); + outCharValue = decodeSurrogatePair(c1, data[w1].Char.UnicodeChar); + } + } else { + outCellCount = w1; + outCharValue = c1; + } +} + +} // anonymous namespace + +void Terminal::reset(SendClearFlag sendClearFirst, int64_t newLine) +{ + if (sendClearFirst == SendClear && !m_plainMode) { + // 0m ==> reset SGR parameters + // 1;1H ==> move cursor to top-left position + // 2J ==> clear the entire screen + m_output.write(CSI "0m" CSI "1;1H" CSI "2J"); + } + m_remoteLine = newLine; + m_remoteColumn = 0; + m_lineData.clear(); + m_cursorHidden = false; + m_remoteColor = -1; +} + +void Terminal::sendLine(int64_t line, const CHAR_INFO *lineData, int width, + int cursorColumn) +{ + ASSERT(width >= 1); + + moveTerminalToLine(line); + + // If possible, see if we can append to what we've already output for this + // line. + if (m_lineDataValid) { + ASSERT(m_lineData.size() == static_cast<size_t>(m_remoteColumn)); + if (m_remoteColumn > 0) { + // In normal mode, if m_lineData.size() equals `width`, then we + // will have trouble outputing the "erase rest of line" command, + // which must be output before reaching the end of the line. In + // plain mode, we don't output that command, so we're OK with a + // full line. + bool okWidth = false; + if (m_plainMode) { + okWidth = static_cast<size_t>(width) >= m_lineData.size(); + } else { + okWidth = static_cast<size_t>(width) > m_lineData.size(); + } + if (!okWidth || + memcmp(m_lineData.data(), lineData, + sizeof(CHAR_INFO) * m_lineData.size()) != 0) { + m_lineDataValid = false; + } + } + } + if (!m_lineDataValid) { + // We can't reuse, so we must reset this line. + hideTerminalCursor(); + if (m_plainMode) { + // We can't backtrack, so repeat this line. + m_output.write("\r\n"); + } else { + m_output.write("\r"); + } + m_lineDataValid = true; + m_lineData.clear(); + m_remoteColumn = 0; + } + + std::string &termLine = m_termLineWorkingBuffer; + termLine.clear(); + size_t trimmedLineLength = 0; + int trimmedCellCount = m_lineData.size(); + bool alreadyErasedLine = false; + + int cellCount = 1; + for (int i = m_lineData.size(); i < width; i += cellCount) { + if (m_outputColor) { + int color = lineData[i].Attributes & COLOR_ATTRIBUTE_MASK; + if (color != m_remoteColor) { + outputSetColor(termLine, color); + trimmedLineLength = termLine.size(); + m_remoteColor = color; + + // All the cells just up to this color change will be output. + trimmedCellCount = i; + } + } + unsigned int ch; + scanUnicodeScalarValue(&lineData[i], width - i, cellCount, ch); + if (ch == ' ') { + // Tentatively add this space character. We'll only output it if + // we see something interesting after it. + termLine.push_back(' '); + } else { + if (i + cellCount == width) { + // We'd like to erase the line after outputting all non-blank + // characters, but this doesn't work if the last cell in the + // line is non-blank. At the point, the cursor is positioned + // just past the end of the line, but in many terminals, + // issuing a CSI 0K at that point also erases the last cell in + // the line. Work around this behavior by issuing the erase + // one character early in that case. + if (!m_plainMode) { + termLine.append(CSI "0K"); // Erase from cursor to EOL + } + alreadyErasedLine = true; + } + ch = fixSpecialCharacters(ch); + char enc[4]; + int enclen = encodeUtf8(enc, ch); + if (enclen == 0) { + enc[0] = '?'; + enclen = 1; + } + termLine.append(enc, enclen); + trimmedLineLength = termLine.size(); + + // All the cells up to and including this cell will be output. + trimmedCellCount = i + cellCount; + } + } + + if (cursorColumn != -1 && trimmedCellCount > cursorColumn) { + // The line content would run past the cursor, so hide it before we + // output. + hideTerminalCursor(); + } + + m_output.write(termLine.data(), trimmedLineLength); + if (!alreadyErasedLine && !m_plainMode) { + m_output.write(CSI "0K"); // Erase from cursor to EOL + } + + ASSERT(trimmedCellCount <= width); + m_lineData.insert(m_lineData.end(), + &lineData[m_lineData.size()], + &lineData[trimmedCellCount]); + m_remoteColumn = trimmedCellCount; +} + +void Terminal::showTerminalCursor(int column, int64_t line) +{ + moveTerminalToLine(line); + if (!m_plainMode) { + if (m_remoteColumn != column) { + char buffer[32]; + winpty_snprintf(buffer, CSI "%dG", column + 1); + m_output.write(buffer); + m_lineDataValid = (column == 0); + m_lineData.clear(); + m_remoteColumn = column; + } + if (m_cursorHidden) { + m_output.write(CSI "?25h"); + m_cursorHidden = false; + } + } +} + +void Terminal::hideTerminalCursor() +{ + if (!m_plainMode) { + if (m_cursorHidden) { + return; + } + m_output.write(CSI "?25l"); + m_cursorHidden = true; + } +} + +void Terminal::moveTerminalToLine(int64_t line) +{ + if (line == m_remoteLine) { + return; + } + + // Do not use CPL or CNL. Konsole 2.5.4 does not support Cursor Previous + // Line (CPL) -- there are "Undecodable sequence" errors. gnome-terminal + // 2.32.0 does handle it. Cursor Next Line (CNL) does nothing if the + // cursor is on the last line already. + + hideTerminalCursor(); + + if (line < m_remoteLine) { + if (m_plainMode) { + // We can't backtrack, so instead repeat the lines again. + m_output.write("\r\n"); + m_remoteLine = line; + } else { + // Backtrack and overwrite previous lines. + // CUrsor Up (CUU) + char buffer[32]; + winpty_snprintf(buffer, "\r" CSI "%uA", + static_cast<unsigned int>(m_remoteLine - line)); + m_output.write(buffer); + m_remoteLine = line; + } + } else if (line > m_remoteLine) { + while (line > m_remoteLine) { + m_output.write("\r\n"); + m_remoteLine++; + } + } + + m_lineDataValid = true; + m_lineData.clear(); + m_remoteColumn = 0; +} + +void Terminal::enableMouseMode(bool enabled) +{ + if (m_mouseModeEnabled == enabled || m_plainMode) { + return; + } + m_mouseModeEnabled = enabled; + if (enabled) { + // Start by disabling UTF-8 coordinate mode (1005), just in case we + // have a terminal that does not support 1006/1015 modes, and 1005 + // happens to be enabled. The UTF-8 coordinates can't be unambiguously + // decoded. + // + // Enable basic mouse support first (1000), then try to switch to + // button-move mode (1002), then try full mouse-move mode (1003). + // Terminals that don't support a mode will be stuck at the highest + // mode they do support. + // + // Enable encoding mode 1015 first, then try to switch to 1006. On + // some terminals, both modes will be enabled, but 1006 will have + // priority. On other terminals, 1006 wins because it's listed last. + // + // See misc/MouseInputNotes.txt for details. + m_output.write( + CSI "?1005l" + CSI "?1000h" CSI "?1002h" CSI "?1003h" CSI "?1015h" CSI "?1006h"); + } else { + // Resetting both encoding modes (1006 and 1015) is necessary, but + // apparently we only need to use reset on one of the 100[023] modes. + // Doing both doesn't hurt. + m_output.write( + CSI "?1006l" CSI "?1015l" CSI "?1003l" CSI "?1002l" CSI "?1000l"); + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/Terminal.h b/src/libs/3rdparty/winpty/src/agent/Terminal.h new file mode 100644 index 0000000000..058eb2650e --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Terminal.h @@ -0,0 +1,69 @@ +// Copyright (c) 2011-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 TERMINAL_H +#define TERMINAL_H + +#include <windows.h> +#include <stdint.h> + +#include <string> +#include <vector> + +#include "Coord.h" + +class NamedPipe; + +class Terminal +{ +public: + explicit Terminal(NamedPipe &output, bool plainMode, bool outputColor) + : m_output(output), m_plainMode(plainMode), m_outputColor(outputColor) + { + } + + enum SendClearFlag { OmitClear, SendClear }; + void reset(SendClearFlag sendClearFirst, int64_t newLine); + void sendLine(int64_t line, const CHAR_INFO *lineData, int width, + int cursorColumn); + void showTerminalCursor(int column, int64_t line); + void hideTerminalCursor(); + +private: + void moveTerminalToLine(int64_t line); + +public: + void enableMouseMode(bool enabled); + +private: + NamedPipe &m_output; + int64_t m_remoteLine = 0; + int m_remoteColumn = 0; + bool m_lineDataValid = true; + std::vector<CHAR_INFO> m_lineData; + bool m_cursorHidden = false; + int m_remoteColor = -1; + std::string m_termLineWorkingBuffer; + bool m_plainMode = false; + bool m_outputColor = true; + bool m_mouseModeEnabled = false; +}; + +#endif // TERMINAL_H diff --git a/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h b/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h new file mode 100644 index 0000000000..6b0de3eff9 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h @@ -0,0 +1,157 @@ +// 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 UNICODE_ENCODING_H +#define UNICODE_ENCODING_H + +#include <stdint.h> + +// Encode the Unicode codepoint with UTF-8. The buffer must be at least 4 +// bytes in size. +static inline int encodeUtf8(char *out, uint32_t code) { + if (code < 0x80) { + out[0] = code; + return 1; + } else if (code < 0x800) { + out[0] = ((code >> 6) & 0x1F) | 0xC0; + out[1] = ((code >> 0) & 0x3F) | 0x80; + return 2; + } else if (code < 0x10000) { + if (code >= 0xD800 && code <= 0xDFFF) { + // The code points 0xD800 to 0xDFFF are reserved for UTF-16 + // surrogate pairs and do not have an encoding in UTF-8. + return 0; + } + out[0] = ((code >> 12) & 0x0F) | 0xE0; + out[1] = ((code >> 6) & 0x3F) | 0x80; + out[2] = ((code >> 0) & 0x3F) | 0x80; + return 3; + } else if (code < 0x110000) { + out[0] = ((code >> 18) & 0x07) | 0xF0; + out[1] = ((code >> 12) & 0x3F) | 0x80; + out[2] = ((code >> 6) & 0x3F) | 0x80; + out[3] = ((code >> 0) & 0x3F) | 0x80; + return 4; + } else { + // Encoding error + return 0; + } +} + +// Encode the Unicode codepoint with UTF-16. The buffer must be large enough +// to hold the output -- either 1 or 2 elements. +static inline int encodeUtf16(wchar_t *out, uint32_t code) { + if (code < 0x10000) { + if (code >= 0xD800 && code <= 0xDFFF) { + // The code points 0xD800 to 0xDFFF are reserved for UTF-16 + // surrogate pairs and do not have an encoding in UTF-16. + return 0; + } + out[0] = code; + return 1; + } else if (code < 0x110000) { + code -= 0x10000; + out[0] = 0xD800 | (code >> 10); + out[1] = 0xDC00 | (code & 0x3FF); + return 2; + } else { + // Encoding error + return 0; + } +} + +// Return the byte size of a UTF-8 character using the value of the first +// byte. +static inline int utf8CharLength(char firstByte) { + // This code would probably be faster if it used __builtin_clz. + if ((firstByte & 0x80) == 0) { + return 1; + } else if ((firstByte & 0xE0) == 0xC0) { + return 2; + } else if ((firstByte & 0xF0) == 0xE0) { + return 3; + } else if ((firstByte & 0xF8) == 0xF0) { + return 4; + } else { + // Malformed UTF-8. + return 0; + } +} + +// The pointer must point to 1-4 bytes, as indicated by the first byte. +// Returns -1 on decoding error. +static inline uint32_t decodeUtf8(const char *in) { + const uint32_t kInvalid = static_cast<uint32_t>(-1); + switch (utf8CharLength(in[0])) { + case 1: { + return in[0]; + } + case 2: { + if ((in[1] & 0xC0) != 0x80) { + return kInvalid; + } + uint32_t tmp = 0; + tmp = (in[0] & 0x1F) << 6; + tmp |= (in[1] & 0x3F); + return tmp <= 0x7F ? kInvalid : tmp; + } + case 3: { + if ((in[1] & 0xC0) != 0x80 || + (in[2] & 0xC0) != 0x80) { + return kInvalid; + } + uint32_t tmp = 0; + tmp = (in[0] & 0x0F) << 12; + tmp |= (in[1] & 0x3F) << 6; + tmp |= (in[2] & 0x3F); + if (tmp <= 0x07FF || (tmp >= 0xD800 && tmp <= 0xDFFF)) { + return kInvalid; + } else { + return tmp; + } + } + case 4: { + if ((in[1] & 0xC0) != 0x80 || + (in[2] & 0xC0) != 0x80 || + (in[3] & 0xC0) != 0x80) { + return kInvalid; + } + uint32_t tmp = 0; + tmp = (in[0] & 0x07) << 18; + tmp |= (in[1] & 0x3F) << 12; + tmp |= (in[2] & 0x3F) << 6; + tmp |= (in[3] & 0x3F); + if (tmp <= 0xFFFF || tmp > 0x10FFFF) { + return kInvalid; + } else { + return tmp; + } + } + default: { + return kInvalid; + } + } +} + +static inline uint32_t decodeSurrogatePair(wchar_t ch1, wchar_t ch2) { + return ((ch1 - 0xD800) << 10) + (ch2 - 0xDC00) + 0x10000; +} + +#endif // UNICODE_ENCODING_H diff --git a/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc b/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc new file mode 100644 index 0000000000..cd4abeb191 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc @@ -0,0 +1,189 @@ +// 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. + +// Encode every code-point using this module and verify that it matches the +// encoding generated using Windows WideCharToMultiByte. + +#include "UnicodeEncoding.h" + +#include <windows.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +static void correctnessByCode() +{ + char mbstr1[4]; + char mbstr2[4]; + wchar_t wch[2]; + for (unsigned int code = 0; code < 0x110000; ++code) { + + // Surrogate pair reserved region. + const bool isReserved = (code >= 0xD800 && code <= 0xDFFF); + + int mblen1 = encodeUtf8(mbstr1, code); + if (isReserved ? mblen1 != 0 : mblen1 <= 0) { + printf("Error: 0x%04X: mblen1=%d\n", code, mblen1); + continue; + } + + int wlen = encodeUtf16(wch, code); + if (isReserved ? wlen != 0 : wlen <= 0) { + printf("Error: 0x%04X: wlen=%d\n", code, wlen); + continue; + } + + if (isReserved) { + continue; + } + + if (mblen1 != utf8CharLength(mbstr1[0])) { + printf("Error: 0x%04X: mblen1=%d, utf8CharLength(mbstr1[0])=%d\n", + code, mblen1, utf8CharLength(mbstr1[0])); + continue; + } + + if (code != decodeUtf8(mbstr1)) { + printf("Error: 0x%04X: decodeUtf8(mbstr1)=%u\n", + code, decodeUtf8(mbstr1)); + continue; + } + + int mblen2 = WideCharToMultiByte(CP_UTF8, 0, wch, wlen, mbstr2, 4, NULL, NULL); + if (mblen1 != mblen2) { + printf("Error: 0x%04X: mblen1=%d, mblen2=%d\n", code, mblen1, mblen2); + continue; + } + + if (memcmp(mbstr1, mbstr2, mblen1) != 0) { + printf("Error: 0x%04x: encodings are different\n", code); + continue; + } + } +} + +static const char *encodingStr(char (&output)[128], char (&buf)[4]) +{ + sprintf(output, "Encoding %02X %02X %02X %02X", + static_cast<uint8_t>(buf[0]), + static_cast<uint8_t>(buf[1]), + static_cast<uint8_t>(buf[2]), + static_cast<uint8_t>(buf[3])); + return output; +} + +// This test can take a couple of minutes to run. +static void correctnessByUtf8Encoding() +{ + for (uint64_t encoding = 0; encoding <= 0xFFFFFFFF; ++encoding) { + + char mb[4]; + mb[0] = encoding; + mb[1] = encoding >> 8; + mb[2] = encoding >> 16; + mb[3] = encoding >> 24; + + const int mblen = utf8CharLength(mb[0]); + if (mblen == 0) { + continue; + } + + // Test this module. + const uint32_t code1 = decodeUtf8(mb); + wchar_t ws1[2] = {}; + const int wslen1 = encodeUtf16(ws1, code1); + + // Test using Windows. We can't decode a codepoint directly; we have + // to do UTF8->UTF16, then decode the surrogate pair. + wchar_t ws2[2] = {}; + const int wslen2 = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, mb, mblen, ws2, 2); + const uint32_t code2 = + (wslen2 == 1 ? ws2[0] : + wslen2 == 2 ? decodeSurrogatePair(ws2[0], ws2[1]) : + static_cast<uint32_t>(-1)); + + // Verify that the two implementations match. + char prefix[128]; + if (code1 != code2) { + printf("%s: code1=0x%04x code2=0x%04x\n", + encodingStr(prefix, mb), + code1, code2); + continue; + } + if (wslen1 != wslen2) { + printf("%s: wslen1=%d wslen2=%d\n", + encodingStr(prefix, mb), + wslen1, wslen2); + continue; + } + if (memcmp(ws1, ws2, wslen1 * sizeof(wchar_t)) != 0) { + printf("%s: ws1 != ws2\n", encodingStr(prefix, mb)); + continue; + } + } +} + +wchar_t g_wch_TEST[] = { 0xD840, 0xDC00 }; +char g_ch_TEST[4]; +wchar_t *volatile g_pwch = g_wch_TEST; +char *volatile g_pch = g_ch_TEST; +unsigned int volatile g_code = 0xA2000; + +static void performance() +{ + { + clock_t start = clock(); + for (long long i = 0; i < 250000000LL; ++i) { + int mblen = WideCharToMultiByte(CP_UTF8, 0, g_pwch, 2, g_pch, 4, NULL, NULL); + assert(mblen == 4); + } + clock_t stop = clock(); + printf("%.3fns per char\n", (double)(stop - start) / CLOCKS_PER_SEC * 4.0); + } + + { + clock_t start = clock(); + for (long long i = 0; i < 3000000000LL; ++i) { + int mblen = encodeUtf8(g_pch, g_code); + assert(mblen == 4); + } + clock_t stop = clock(); + printf("%.3fns per char\n", (double)(stop - start) / CLOCKS_PER_SEC / 3.0); + } +} + +int main() +{ + printf("Testing correctnessByCode...\n"); + fflush(stdout); + correctnessByCode(); + + printf("Testing correctnessByUtf8Encoding... (may take a couple minutes)\n"); + fflush(stdout); + correctnessByUtf8Encoding(); + + printf("Testing performance...\n"); + fflush(stdout); + performance(); + + return 0; +} diff --git a/src/libs/3rdparty/winpty/src/agent/Win32Console.cc b/src/libs/3rdparty/winpty/src/agent/Win32Console.cc new file mode 100644 index 0000000000..d53de021f5 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Win32Console.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2011-2016 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 "Win32Console.h" + +#include <windows.h> +#include <wchar.h> + +#include <string> + +#include "../shared/DebugClient.h" +#include "../shared/WinptyAssert.h" + +Win32Console::Win32Console() : m_titleWorkBuf(16) +{ + // The console window must be non-NULL. It is used for two purposes: + // (1) "Freezing" the console to detect the exact number of lines that + // have scrolled. + // (2) Killing processes attached to the console, by posting a WM_CLOSE + // message to the console window. + m_hwnd = GetConsoleWindow(); + ASSERT(m_hwnd != nullptr); +} + +std::wstring Win32Console::title() +{ + while (true) { + // Calling GetConsoleTitleW is tricky, because its behavior changed + // from XP->Vista, then again from Win7->Win8. The Vista+Win7 behavior + // is especially broken. + // + // The MSDN documentation documents nSize as the "size of the buffer + // pointed to by the lpConsoleTitle parameter, in characters" and the + // successful return value as "the length of the console window's + // title, in characters." + // + // On XP, the function returns the title length, AFTER truncation + // (excluding the NUL terminator). If the title is blank, the API + // returns 0 and does not NUL-terminate the buffer. To accommodate + // XP, the function must: + // * Terminate the buffer itself. + // * Double the size of the title buffer in a loop. + // + // On Vista and up, the function returns the non-truncated title + // length (excluding the NUL terminator). + // + // On Vista and Windows 7, there is a bug where the buffer size is + // interpreted as a byte count rather than a wchar_t count. To + // work around this, we must pass GetConsoleTitleW a buffer that is + // twice as large as what is actually needed. + // + // See misc/*/Test_GetConsoleTitleW.cc for tests demonstrating Windows' + // behavior. + + DWORD count = GetConsoleTitleW(m_titleWorkBuf.data(), + m_titleWorkBuf.size()); + const size_t needed = (count + 1) * sizeof(wchar_t); + if (m_titleWorkBuf.size() < needed) { + m_titleWorkBuf.resize(needed); + continue; + } + m_titleWorkBuf[count] = L'\0'; + return m_titleWorkBuf.data(); + } +} + +void Win32Console::setTitle(const std::wstring &title) +{ + if (!SetConsoleTitleW(title.c_str())) { + trace("SetConsoleTitleW failed"); + } +} + +void Win32Console::setFrozen(bool frozen) { + const int SC_CONSOLE_MARK = 0xFFF2; + const int SC_CONSOLE_SELECT_ALL = 0xFFF5; + if (frozen == m_frozen) { + // Do nothing. + } else if (frozen) { + // Enter selection mode by activating either Mark or SelectAll. + const int command = m_freezeUsesMark ? SC_CONSOLE_MARK + : SC_CONSOLE_SELECT_ALL; + SendMessage(m_hwnd, WM_SYSCOMMAND, command, 0); + m_frozen = true; + } else { + // Send Escape to cancel the selection. + SendMessage(m_hwnd, WM_CHAR, 27, 0x00010001); + m_frozen = false; + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/Win32Console.h b/src/libs/3rdparty/winpty/src/agent/Win32Console.h new file mode 100644 index 0000000000..ed83877e99 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Win32Console.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011-2016 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_WIN32_CONSOLE_H +#define AGENT_WIN32_CONSOLE_H + +#include <windows.h> + +#include <string> +#include <vector> + +class Win32Console +{ +public: + class FreezeGuard { + public: + FreezeGuard(Win32Console &console, bool frozen) : + m_console(console), m_previous(console.frozen()) { + m_console.setFrozen(frozen); + } + ~FreezeGuard() { + m_console.setFrozen(m_previous); + } + FreezeGuard(const FreezeGuard &other) = delete; + FreezeGuard &operator=(const FreezeGuard &other) = delete; + private: + Win32Console &m_console; + bool m_previous; + }; + + Win32Console(); + + HWND hwnd() { return m_hwnd; } + std::wstring title(); + void setTitle(const std::wstring &title); + void setFreezeUsesMark(bool useMark) { m_freezeUsesMark = useMark; } + void setNewW10(bool isNewW10) { m_isNewW10 = isNewW10; } + bool isNewW10() { return m_isNewW10; } + void setFrozen(bool frozen=true); + bool frozen() { return m_frozen; } + +private: + HWND m_hwnd = nullptr; + bool m_frozen = false; + bool m_freezeUsesMark = false; + bool m_isNewW10 = false; + std::vector<wchar_t> m_titleWorkBuf; +}; + +#endif // AGENT_WIN32_CONSOLE_H diff --git a/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc new file mode 100644 index 0000000000..ed93f4081f --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2011-2016 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 "Win32ConsoleBuffer.h" + +#include <windows.h> + +#include "../shared/DebugClient.h" +#include "../shared/StringBuilder.h" +#include "../shared/WinptyAssert.h" + +std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::openStdout() { + return std::unique_ptr<Win32ConsoleBuffer>( + new Win32ConsoleBuffer(GetStdHandle(STD_OUTPUT_HANDLE), false)); +} + +std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::openConout() { + const HANDLE conout = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + ASSERT(conout != INVALID_HANDLE_VALUE); + return std::unique_ptr<Win32ConsoleBuffer>( + new Win32ConsoleBuffer(conout, true)); +} + +std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::createErrorBuffer() { + SECURITY_ATTRIBUTES sa = {}; + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + const HANDLE conout = + CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + CONSOLE_TEXTMODE_BUFFER, + nullptr); + ASSERT(conout != INVALID_HANDLE_VALUE); + return std::unique_ptr<Win32ConsoleBuffer>( + new Win32ConsoleBuffer(conout, true)); +} + +HANDLE Win32ConsoleBuffer::conout() { + return m_conout; +} + +void Win32ConsoleBuffer::clearLines( + int row, + int count, + const ConsoleScreenBufferInfo &info) { + // TODO: error handling + const int width = info.bufferSize().X; + DWORD actual = 0; + if (!FillConsoleOutputCharacterW( + m_conout, L' ', width * count, Coord(0, row), + &actual) || static_cast<int>(actual) != width * count) { + trace("FillConsoleOutputCharacterW failed"); + } + if (!FillConsoleOutputAttribute( + m_conout, kDefaultAttributes, width * count, Coord(0, row), + &actual) || static_cast<int>(actual) != width * count) { + trace("FillConsoleOutputAttribute failed"); + } +} + +void Win32ConsoleBuffer::clearAllLines(const ConsoleScreenBufferInfo &info) { + clearLines(0, info.bufferSize().Y, info); +} + +ConsoleScreenBufferInfo Win32ConsoleBuffer::bufferInfo() { + // TODO: error handling + ConsoleScreenBufferInfo info; + if (!GetConsoleScreenBufferInfo(m_conout, &info)) { + trace("GetConsoleScreenBufferInfo failed"); + } + return info; +} + +Coord Win32ConsoleBuffer::bufferSize() { + return bufferInfo().bufferSize(); +} + +SmallRect Win32ConsoleBuffer::windowRect() { + return bufferInfo().windowRect(); +} + +bool Win32ConsoleBuffer::resizeBufferRange(const Coord &initialSize, + Coord &finalSize) { + if (SetConsoleScreenBufferSize(m_conout, initialSize)) { + finalSize = initialSize; + return true; + } + // The font might be too small to accommodate a very narrow console window. + // In that case, rather than simply give up, it's better to try wider + // buffer sizes until the call succeeds. + Coord size = initialSize; + while (size.X < 20) { + size.X++; + if (SetConsoleScreenBufferSize(m_conout, size)) { + finalSize = size; + trace("SetConsoleScreenBufferSize: initial size (%d,%d) failed, " + "but wider size (%d,%d) succeeded", + initialSize.X, initialSize.Y, + finalSize.X, finalSize.Y); + return true; + } + } + trace("SetConsoleScreenBufferSize failed: " + "tried (%d,%d) through (%d,%d)", + initialSize.X, initialSize.Y, + size.X, size.Y); + return false; +} + +void Win32ConsoleBuffer::resizeBuffer(const Coord &size) { + // TODO: error handling + if (!SetConsoleScreenBufferSize(m_conout, size)) { + trace("SetConsoleScreenBufferSize failed: size=(%d,%d)", + size.X, size.Y); + } +} + +void Win32ConsoleBuffer::moveWindow(const SmallRect &rect) { + // TODO: error handling + if (!SetConsoleWindowInfo(m_conout, TRUE, &rect)) { + trace("SetConsoleWindowInfo failed"); + } +} + +Coord Win32ConsoleBuffer::cursorPosition() { + return bufferInfo().dwCursorPosition; +} + +void Win32ConsoleBuffer::setCursorPosition(const Coord &coord) { + // TODO: error handling + if (!SetConsoleCursorPosition(m_conout, coord)) { + trace("SetConsoleCursorPosition failed"); + } +} + +void Win32ConsoleBuffer::read(const SmallRect &rect, CHAR_INFO *data) { + // TODO: error handling + SmallRect tmp(rect); + if (!ReadConsoleOutputW(m_conout, data, rect.size(), Coord(), &tmp) && + isTracingEnabled()) { + StringBuilder sb(256); + auto outStruct = [&](const SMALL_RECT &sr) { + sb << "{L=" << sr.Left << ",T=" << sr.Top + << ",R=" << sr.Right << ",B=" << sr.Bottom << '}'; + }; + sb << "Win32ConsoleBuffer::read: ReadConsoleOutput failed: readRegion="; + outStruct(rect); + CONSOLE_SCREEN_BUFFER_INFO info = {}; + if (GetConsoleScreenBufferInfo(m_conout, &info)) { + sb << ", dwSize=(" << info.dwSize.X << ',' << info.dwSize.Y + << "), srWindow="; + outStruct(info.srWindow); + } else { + sb << ", GetConsoleScreenBufferInfo also failed"; + } + trace("%s", sb.c_str()); + } +} + +void Win32ConsoleBuffer::write(const SmallRect &rect, const CHAR_INFO *data) { + // TODO: error handling + SmallRect tmp(rect); + if (!WriteConsoleOutputW(m_conout, data, rect.size(), Coord(), &tmp)) { + trace("WriteConsoleOutput failed"); + } +} + +void Win32ConsoleBuffer::setTextAttribute(WORD attributes) { + if (!SetConsoleTextAttribute(m_conout, attributes)) { + trace("SetConsoleTextAttribute failed"); + } +} diff --git a/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h new file mode 100644 index 0000000000..a68d8d304f --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011-2016 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_WIN32_CONSOLE_BUFFER_H +#define AGENT_WIN32_CONSOLE_BUFFER_H + +#include <windows.h> + +#include <string.h> + +#include <memory> + +#include "Coord.h" +#include "SmallRect.h" + +class ConsoleScreenBufferInfo : public CONSOLE_SCREEN_BUFFER_INFO { +public: + ConsoleScreenBufferInfo() + { + memset(this, 0, sizeof(*this)); + } + + Coord bufferSize() const { return dwSize; } + SmallRect windowRect() const { return srWindow; } + Coord cursorPosition() const { return dwCursorPosition; } +}; + +class Win32ConsoleBuffer { +private: + Win32ConsoleBuffer(HANDLE conout, bool owned) : + m_conout(conout), m_owned(owned) + { + } + +public: + static const int kDefaultAttributes = 7; + + ~Win32ConsoleBuffer() { + if (m_owned) { + CloseHandle(m_conout); + } + } + + static std::unique_ptr<Win32ConsoleBuffer> openStdout(); + static std::unique_ptr<Win32ConsoleBuffer> openConout(); + static std::unique_ptr<Win32ConsoleBuffer> createErrorBuffer(); + + Win32ConsoleBuffer(const Win32ConsoleBuffer &other) = delete; + Win32ConsoleBuffer &operator=(const Win32ConsoleBuffer &other) = delete; + + HANDLE conout(); + void clearLines(int row, int count, const ConsoleScreenBufferInfo &info); + void clearAllLines(const ConsoleScreenBufferInfo &info); + + // Buffer and window sizes. + ConsoleScreenBufferInfo bufferInfo(); + Coord bufferSize(); + SmallRect windowRect(); + void resizeBuffer(const Coord &size); + bool resizeBufferRange(const Coord &initialSize, Coord &finalSize); + bool resizeBufferRange(const Coord &initialSize) { + Coord dummy; + return resizeBufferRange(initialSize, dummy); + } + void moveWindow(const SmallRect &rect); + + // Cursor. + Coord cursorPosition(); + void setCursorPosition(const Coord &point); + + // Screen content. + void read(const SmallRect &rect, CHAR_INFO *data); + void write(const SmallRect &rect, const CHAR_INFO *data); + + void setTextAttribute(WORD attributes); + +private: + HANDLE m_conout = nullptr; + bool m_owned = false; +}; + +#endif // AGENT_WIN32_CONSOLE_BUFFER_H diff --git a/src/libs/3rdparty/winpty/src/agent/main.cc b/src/libs/3rdparty/winpty/src/agent/main.cc new file mode 100644 index 0000000000..427cb3a3aa --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/main.cc @@ -0,0 +1,120 @@ +// Copyright (c) 2011-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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <wchar.h> +#include <shellapi.h> + +#include "../shared/StringUtil.h" +#include "../shared/WindowsVersion.h" +#include "../shared/WinptyAssert.h" +#include "../shared/WinptyVersion.h" + +#include "Agent.h" +#include "AgentCreateDesktop.h" +#include "DebugShowInput.h" + +const char USAGE[] = +"Usage: %ls controlPipeName flags mouseMode cols rows\n" +"Usage: %ls controlPipeName --create-desktop\n" +"\n" +"Ordinarily, this program is launched by winpty.dll and is not directly\n" +"useful to winpty users. However, it also has options intended for\n" +"debugging winpty.\n" +"\n" +"Usage: %ls [options]\n" +"\n" +"Options:\n" +" --show-input [--with-mouse] [--escape-input]\n" +" Dump INPUT_RECORDs from the console input buffer\n" +" --with-mouse: Include MOUSE_INPUT_RECORDs in the dump\n" +" output\n" +" --escape-input: Direct the new Windows 10 console to use\n" +" escape sequences for input\n" +" --version Print the winpty version\n"; + +static uint64_t winpty_atoi64(const char *str) { + return strtoll(str, NULL, 10); +} + +int main() { + dumpWindowsVersion(); + dumpVersionToTrace(); + + // Technically, we should free the CommandLineToArgvW return value using + // a single call to LocalFree, but the call will never actually happen in + // the normal case. + int argc = 0; + wchar_t *cmdline = GetCommandLineW(); + ASSERT(cmdline != nullptr && "GetCommandLineW returned NULL"); + wchar_t **argv = CommandLineToArgvW(cmdline, &argc); + ASSERT(argv != nullptr && "CommandLineToArgvW returned NULL"); + + if (argc == 2 && !wcscmp(argv[1], L"--version")) { + dumpVersionToStdout(); + return 0; + } + + if (argc >= 2 && !wcscmp(argv[1], L"--show-input")) { + bool withMouse = false; + bool escapeInput = false; + for (int i = 2; i < argc; ++i) { + if (!wcscmp(argv[i], L"--with-mouse")) { + withMouse = true; + } else if (!wcscmp(argv[i], L"--escape-input")) { + escapeInput = true; + } else { + fprintf(stderr, "Unrecognized --show-input option: %ls\n", + argv[i]); + return 1; + } + } + debugShowInput(withMouse, escapeInput); + return 0; + } + + if (argc == 3 && !wcscmp(argv[2], L"--create-desktop")) { + handleCreateDesktop(argv[1]); + return 0; + } + + if (argc != 6) { + fprintf(stderr, USAGE, argv[0], argv[0], argv[0]); + return 1; + } + + Agent agent(argv[1], + winpty_atoi64(utf8FromWide(argv[2]).c_str()), + atoi(utf8FromWide(argv[3]).c_str()), + atoi(utf8FromWide(argv[4]).c_str()), + atoi(utf8FromWide(argv[5]).c_str())); + agent.run(); + + // The Agent destructor shouldn't return, but if it does, exit + // unsuccessfully. + return 1; +} diff --git a/src/libs/3rdparty/winpty/src/agent/subdir.mk b/src/libs/3rdparty/winpty/src/agent/subdir.mk new file mode 100644 index 0000000000..1c7d37e3e5 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/agent/subdir.mk @@ -0,0 +1,61 @@ +# Copyright (c) 2011-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. + +ALL_TARGETS += build/winpty-agent.exe + +$(eval $(call def_mingw_target,agent,-DWINPTY_AGENT_ASSERT)) + +AGENT_OBJECTS = \ + build/agent/agent/Agent.o \ + build/agent/agent/AgentCreateDesktop.o \ + build/agent/agent/ConsoleFont.o \ + build/agent/agent/ConsoleInput.o \ + build/agent/agent/ConsoleInputReencoding.o \ + build/agent/agent/ConsoleLine.o \ + build/agent/agent/DebugShowInput.o \ + build/agent/agent/DefaultInputMap.o \ + build/agent/agent/EventLoop.o \ + build/agent/agent/InputMap.o \ + build/agent/agent/LargeConsoleRead.o \ + build/agent/agent/NamedPipe.o \ + build/agent/agent/Scraper.o \ + build/agent/agent/Terminal.o \ + build/agent/agent/Win32Console.o \ + build/agent/agent/Win32ConsoleBuffer.o \ + build/agent/agent/main.o \ + build/agent/shared/BackgroundDesktop.o \ + build/agent/shared/Buffer.o \ + build/agent/shared/DebugClient.o \ + build/agent/shared/GenRandom.o \ + build/agent/shared/OwnedHandle.o \ + build/agent/shared/StringUtil.o \ + build/agent/shared/WindowsSecurity.o \ + build/agent/shared/WindowsVersion.o \ + build/agent/shared/WinptyAssert.o \ + build/agent/shared/WinptyException.o \ + build/agent/shared/WinptyVersion.o + +build/agent/shared/WinptyVersion.o : build/gen/GenVersion.h + +build/winpty-agent.exe : $(AGENT_OBJECTS) + $(info Linking $@) + @$(MINGW_CXX) $(MINGW_LDFLAGS) -o $@ $^ + +-include $(AGENT_OBJECTS:.o=.d) diff --git a/src/libs/3rdparty/winpty/src/configurations.gypi b/src/libs/3rdparty/winpty/src/configurations.gypi new file mode 100644 index 0000000000..e990a60338 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/configurations.gypi @@ -0,0 +1,60 @@ +# By default gyp/msbuild build for 32-bit Windows. This gyp include file +# defines configurations for both 32-bit and 64-bit Windows. To use it, run: +# +# C:\...\winpty\src>gyp -I configurations.gypi +# +# This command generates Visual Studio project files with a Release +# configuration and two Platforms--Win32 and x64. Both can be built: +# +# C:\...\winpty\src>msbuild winpty.sln /p:Platform=Win32 +# C:\...\winpty\src>msbuild winpty.sln /p:Platform=x64 +# +# The output is placed in: +# +# C:\...\winpty\src\Release\Win32 +# C:\...\winpty\src\Release\x64 +# +# Windows XP note: By default, the project files will use the default "toolset" +# for the given MSVC version. For MSVC 2013 and MSVC 2015, the default toolset +# generates binaries that do not run on Windows XP. To target Windows XP, +# select the XP-specific toolset by passing +# -D WINPTY_MSBUILD_TOOLSET={v120_xp,v140_xp} to gyp (v120_xp == MSVC 2013, +# v140_xp == MSVC 2015). Unfortunately, it isn't possible to have a single +# project file with configurations for both XP and post-XP. This seems to be a +# limitation of the MSVC project file format. +# +# This file is not included by default, because I suspect it would interfere +# with node-gyp, which has a different system for building 32-vs-64-bit +# binaries. It uses a common.gypi, and the project files it generates can only +# build a single architecture, the output paths are not differentiated by +# architecture. + +{ + 'variables': { + 'WINPTY_MSBUILD_TOOLSET%': '', + }, + 'target_defaults': { + 'default_configuration': 'Release_Win32', + 'configurations': { + 'Release_Win32': { + 'msvs_configuration_platform': 'Win32', + }, + 'Release_x64': { + 'msvs_configuration_platform': 'x64', + }, + }, + 'msvs_configuration_attributes': { + 'OutputDirectory': '$(SolutionDir)$(ConfigurationName)\\$(Platform)', + 'IntermediateDirectory': '$(ConfigurationName)\\$(Platform)\\obj\\$(ProjectName)', + }, + 'msvs_settings': { + 'VCLinkerTool': { + 'SubSystem': '1', # /SUBSYSTEM:CONSOLE + }, + 'VCCLCompilerTool': { + 'RuntimeLibrary': '0', # MultiThreaded (/MT) + }, + }, + 'msbuild_toolset' : '<(WINPTY_MSBUILD_TOOLSET)', + } +} diff --git a/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc b/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc new file mode 100644 index 0000000000..353d31c1c6 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc @@ -0,0 +1,117 @@ +// 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 <cstdio> +#include <cstdlib> + +#include <windows.h> + +#include "../shared/WindowsSecurity.h" +#include "../shared/WinptyException.h" + +const wchar_t *kPipeName = L"\\\\.\\pipe\\DebugServer"; + +// A message may not be larger than this size. +const int MSG_SIZE = 4096; + +static void usage(const char *program, int code) { + printf("Usage: %s [--everyone]\n" + "\n" + "Creates the named pipe %ls and reads messages. Prints each\n" + "message to stdout. By default, only the current user can send messages.\n" + "Pass --everyone to let anyone send a message.\n" + "\n" + "Use the WINPTY_DEBUG environment variable to enable winpty trace output.\n" + "(e.g. WINPTY_DEBUG=trace for the default trace output.) Set WINPTYDBG=1\n" + "to enable trace with older winpty versions.\n", + program, kPipeName); + exit(code); +} + +int main(int argc, char *argv[]) { + bool everyone = false; + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--everyone") { + everyone = true; + } else if (arg == "-h" || arg == "--help") { + usage(argv[0], 0); + } else { + usage(argv[0], 1); + } + } + + SecurityDescriptor sd; + PSECURITY_ATTRIBUTES psa = nullptr; + SECURITY_ATTRIBUTES sa = {}; + if (everyone) { + try { + sd = createPipeSecurityDescriptorOwnerFullControlEveryoneWrite(); + } catch (const WinptyException &e) { + fprintf(stderr, + "error creating security descriptor: %ls\n", e.what()); + exit(1); + } + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = sd.get(); + psa = &sa; + } + + HANDLE serverPipe = CreateNamedPipeW( + kPipeName, + /*dwOpenMode=*/PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + /*dwPipeMode=*/PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | + rejectRemoteClientsPipeFlag(), + /*nMaxInstances=*/1, + /*nOutBufferSize=*/MSG_SIZE, + /*nInBufferSize=*/MSG_SIZE, + /*nDefaultTimeOut=*/10 * 1000, + psa); + + if (serverPipe == INVALID_HANDLE_VALUE) { + fprintf(stderr, "error: could not create %ls pipe: error %u\n", + kPipeName, static_cast<unsigned>(GetLastError())); + exit(1); + } + + char msgBuffer[MSG_SIZE + 1]; + + while (true) { + if (!ConnectNamedPipe(serverPipe, nullptr)) { + fprintf(stderr, "error: ConnectNamedPipe failed\n"); + fflush(stderr); + exit(1); + } + DWORD bytesRead = 0; + if (!ReadFile(serverPipe, msgBuffer, MSG_SIZE, &bytesRead, nullptr)) { + fprintf(stderr, "error: ReadFile on pipe failed\n"); + fflush(stderr); + DisconnectNamedPipe(serverPipe); + continue; + } + msgBuffer[bytesRead] = '\n'; + fwrite(msgBuffer, 1, bytesRead + 1, stdout); + fflush(stdout); + + DWORD bytesWritten = 0; + WriteFile(serverPipe, "OK", 2, &bytesWritten, nullptr); + DisconnectNamedPipe(serverPipe); + } +} diff --git a/src/libs/3rdparty/winpty/src/debugserver/subdir.mk b/src/libs/3rdparty/winpty/src/debugserver/subdir.mk new file mode 100644 index 0000000000..beed1bd597 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/debugserver/subdir.mk @@ -0,0 +1,41 @@ +# 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. + +ALL_TARGETS += build/winpty-debugserver.exe + +$(eval $(call def_mingw_target,debugserver,)) + +DEBUGSERVER_OBJECTS = \ + build/debugserver/debugserver/DebugServer.o \ + build/debugserver/shared/DebugClient.o \ + build/debugserver/shared/OwnedHandle.o \ + build/debugserver/shared/StringUtil.o \ + build/debugserver/shared/WindowsSecurity.o \ + build/debugserver/shared/WindowsVersion.o \ + build/debugserver/shared/WinptyAssert.o \ + build/debugserver/shared/WinptyException.o + +build/debugserver/shared/WindowsVersion.o : build/gen/GenVersion.h + +build/winpty-debugserver.exe : $(DEBUGSERVER_OBJECTS) + $(info Linking $@) + @$(MINGW_CXX) $(MINGW_LDFLAGS) -o $@ $^ + +-include $(DEBUGSERVER_OBJECTS:.o=.d) diff --git a/src/libs/3rdparty/winpty/src/include/winpty.h b/src/libs/3rdparty/winpty/src/include/winpty.h new file mode 100644 index 0000000000..fdfe4bca21 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/include/winpty.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2011-2016 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 WINPTY_H +#define WINPTY_H + +#include <windows.h> + +#include "winpty_constants.h" + +/* On 32-bit Windows, winpty functions have the default __cdecl (not __stdcall) + * calling convention. (64-bit Windows has only a single calling convention.) + * When compiled with __declspec(dllexport), with either MinGW or MSVC, the + * winpty functions are unadorned--no underscore prefix or '@nn' suffix--so + * GetProcAddress can be used easily. */ +#ifdef COMPILING_WINPTY_DLL +#define WINPTY_API __declspec(dllexport) +#else +#define WINPTY_API __declspec(dllimport) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* The winpty API uses wide characters, instead of UTF-8, to avoid conversion + * complications related to surrogates. Windows generally tolerates unpaired + * surrogates in text, which makes conversion to and from UTF-8 ambiguous and + * complicated. (There are different UTF-8 variants that deal with UTF-16 + * surrogates differently.) */ + + + +/***************************************************************************** + * Error handling. */ + +/* All the APIs have an optional winpty_error_t output parameter. If a + * non-NULL argument is specified, then either the API writes NULL to the + * value (on success) or writes a newly allocated winpty_error_t object. The + * object must be freed using winpty_error_free. */ + +/* An error object. */ +typedef struct winpty_error_s winpty_error_t; +typedef winpty_error_t *winpty_error_ptr_t; + +/* An error code -- one of WINPTY_ERROR_xxx. */ +typedef DWORD winpty_result_t; + +/* Gets the error code from the error object. */ +WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err); + +/* Returns a textual representation of the error. The string is freed when + * the error is freed. */ +WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err); + +/* Free the error object. Every error returned from the winpty API must be + * freed. */ +WINPTY_API void winpty_error_free(winpty_error_ptr_t err); + + + +/***************************************************************************** + * Configuration of a new agent. */ + +/* The winpty_config_t object is not thread-safe. */ +typedef struct winpty_config_s winpty_config_t; + +/* Allocate a winpty_config_t value. Returns NULL on error. There are no + * required settings -- the object may immediately be used. agentFlags is a + * set of zero or more WINPTY_FLAG_xxx values. An unrecognized flag results + * in an assertion failure. */ +WINPTY_API winpty_config_t * +winpty_config_new(UINT64 agentFlags, winpty_error_ptr_t *err /*OPTIONAL*/); + +/* Free the cfg object after passing it to winpty_open. */ +WINPTY_API void winpty_config_free(winpty_config_t *cfg); + +WINPTY_API void +winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows); + +/* Set the mouse mode to one of the WINPTY_MOUSE_MODE_xxx constants. */ +WINPTY_API void +winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode); + +/* Amount of time to wait for the agent to startup and to wait for any given + * agent RPC request. Must be greater than 0. Can be INFINITE. */ +WINPTY_API void +winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs); + + + +/***************************************************************************** + * Start the agent. */ + +/* The winpty_t object is thread-safe. */ +typedef struct winpty_s winpty_t; + +/* Starts the agent. Returns NULL on error. This process will connect to the + * agent over a control pipe, and the agent will open data pipes (e.g. CONIN + * and CONOUT). */ +WINPTY_API winpty_t * +winpty_open(const winpty_config_t *cfg, + winpty_error_ptr_t *err /*OPTIONAL*/); + +/* A handle to the agent process. This value is valid for the lifetime of the + * winpty_t object. Do not close it. */ +WINPTY_API HANDLE winpty_agent_process(winpty_t *wp); + + + +/***************************************************************************** + * I/O pipes. */ + +/* Returns the names of named pipes used for terminal I/O. Each input or + * output direction uses a different half-duplex pipe. The agent creates + * these pipes, and the client can connect to them using ordinary I/O methods. + * The strings are freed when the winpty_t object is freed. + * + * winpty_conerr_name returns NULL unless WINPTY_FLAG_CONERR is specified. + * + * N.B.: CreateFile does not block when connecting to a local server pipe. If + * the server pipe does not exist or is already connected, then it fails + * instantly. */ +WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp); +WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp); +WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp); + + + +/***************************************************************************** + * winpty agent RPC call: process creation. */ + +/* The winpty_spawn_config_t object is not thread-safe. */ +typedef struct winpty_spawn_config_s winpty_spawn_config_t; + +/* winpty_spawn_config strings do not need to live as long as the config + * object. They are copied. Returns NULL on error. spawnFlags is a set of + * zero or more WINPTY_SPAWN_FLAG_xxx values. An unrecognized flag results in + * an assertion failure. + * + * env is a a pointer to an environment block like that passed to + * CreateProcess--a contiguous array of NUL-terminated "VAR=VAL" strings + * followed by a final NUL terminator. + * + * N.B.: If you want to gather all of the child's output, you may want the + * WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN flag. + */ +WINPTY_API winpty_spawn_config_t * +winpty_spawn_config_new(UINT64 spawnFlags, + LPCWSTR appname /*OPTIONAL*/, + LPCWSTR cmdline /*OPTIONAL*/, + LPCWSTR cwd /*OPTIONAL*/, + LPCWSTR env /*OPTIONAL*/, + winpty_error_ptr_t *err /*OPTIONAL*/); + +/* Free the cfg object after passing it to winpty_spawn. */ +WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg); + +/* + * Spawns the new process. + * + * The function initializes all output parameters to zero or NULL. + * + * On success, the function returns TRUE. For each of process_handle and + * thread_handle that is non-NULL, the HANDLE returned from CreateProcess is + * duplicated from the agent and returned to the winpty client. The client is + * responsible for closing these HANDLES. + * + * On failure, the function returns FALSE, and if err is non-NULL, then *err + * is set to an error object. + * + * If the agent's CreateProcess call failed, then *create_process_error is set + * to GetLastError(), and the WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED error + * is returned. + * + * winpty_spawn can only be called once per winpty_t object. If it is called + * before the output data pipe(s) is/are connected, then collected output is + * buffered until the pipes are connected, rather than being discarded. + * + * N.B.: GetProcessId works even if the process has exited. The PID is not + * recycled until the NT process object is freed. + * (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803) + */ +WINPTY_API BOOL +winpty_spawn(winpty_t *wp, + const winpty_spawn_config_t *cfg, + HANDLE *process_handle /*OPTIONAL*/, + HANDLE *thread_handle /*OPTIONAL*/, + DWORD *create_process_error /*OPTIONAL*/, + winpty_error_ptr_t *err /*OPTIONAL*/); + + + +/***************************************************************************** + * winpty agent RPC calls: everything else */ + +/* Change the size of the Windows console window. */ +WINPTY_API BOOL +winpty_set_size(winpty_t *wp, int cols, int rows, + winpty_error_ptr_t *err /*OPTIONAL*/); + +/* Gets a list of processes attached to the console. */ +WINPTY_API int +winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount, + winpty_error_ptr_t *err /*OPTIONAL*/); + +/* Frees the winpty_t object and the OS resources contained in it. This + * call breaks the connection with the agent, which should then close its + * console, terminating the processes attached to it. + * + * This function must not be called if any other threads are using the + * winpty_t object. Undefined behavior results. */ +WINPTY_API void winpty_free(winpty_t *wp); + + + +/****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* WINPTY_H */ diff --git a/src/libs/3rdparty/winpty/src/include/winpty_constants.h b/src/libs/3rdparty/winpty/src/include/winpty_constants.h new file mode 100644 index 0000000000..11e34cf171 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/include/winpty_constants.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016 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 WINPTY_CONSTANTS_H +#define WINPTY_CONSTANTS_H + +/* + * You may want to include winpty.h instead, which includes this header. + * + * This file is split out from winpty.h so that the agent can access the + * winpty flags without also declaring the libwinpty APIs. + */ + +/***************************************************************************** + * Error codes. */ + +#define WINPTY_ERROR_SUCCESS 0 +#define WINPTY_ERROR_OUT_OF_MEMORY 1 +#define WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED 2 +#define WINPTY_ERROR_LOST_CONNECTION 3 +#define WINPTY_ERROR_AGENT_EXE_MISSING 4 +#define WINPTY_ERROR_UNSPECIFIED 5 +#define WINPTY_ERROR_AGENT_DIED 6 +#define WINPTY_ERROR_AGENT_TIMEOUT 7 +#define WINPTY_ERROR_AGENT_CREATION_FAILED 8 + + + +/***************************************************************************** + * Configuration of a new agent. */ + +/* Create a new screen buffer (connected to the "conerr" terminal pipe) and + * pass it to child processes as the STDERR handle. This flag also prevents + * the agent from reopening CONOUT$ when it polls -- regardless of whether the + * active screen buffer changes, winpty continues to monitor the original + * primary screen buffer. */ +#define WINPTY_FLAG_CONERR 0x1ull + +/* Don't output escape sequences. */ +#define WINPTY_FLAG_PLAIN_OUTPUT 0x2ull + +/* Do output color escape sequences. These escapes are output by default, but + * are suppressed with WINPTY_FLAG_PLAIN_OUTPUT. Use this flag to reenable + * them. */ +#define WINPTY_FLAG_COLOR_ESCAPES 0x4ull + +/* On XP and Vista, winpty needs to put the hidden console on a desktop in a + * service window station so that its polling does not interfere with other + * (visible) console windows. To create this desktop, it must change the + * process' window station (i.e. SetProcessWindowStation) for the duration of + * the winpty_open call. In theory, this change could interfere with the + * winpty client (e.g. other threads, spawning children), so winpty by default + * spawns a special agent process to create the hidden desktop. Spawning + * processes on Windows is slow, though, so if + * WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION is set, winpty changes this + * process' window station instead. + * See https://github.com/rprichard/winpty/issues/58. */ +#define WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION 0x8ull + +#define WINPTY_FLAG_MASK (0ull \ + | WINPTY_FLAG_CONERR \ + | WINPTY_FLAG_PLAIN_OUTPUT \ + | WINPTY_FLAG_COLOR_ESCAPES \ + | WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION \ +) + +/* QuickEdit mode is initially disabled, and the agent does not send mouse + * mode sequences to the terminal. If it receives mouse input, though, it + * still writes MOUSE_EVENT_RECORD values into CONIN. */ +#define WINPTY_MOUSE_MODE_NONE 0 + +/* QuickEdit mode is initially enabled. As CONIN enters or leaves mouse + * input mode (i.e. where ENABLE_MOUSE_INPUT is on and ENABLE_QUICK_EDIT_MODE + * is off), the agent enables or disables mouse input on the terminal. + * + * This is the default mode. */ +#define WINPTY_MOUSE_MODE_AUTO 1 + +/* QuickEdit mode is initially disabled, and the agent enables the terminal's + * mouse input mode. It does not disable terminal mouse mode (until exit). */ +#define WINPTY_MOUSE_MODE_FORCE 2 + + + +/***************************************************************************** + * winpty agent RPC call: process creation. */ + +/* If the spawn is marked "auto-shutdown", then the agent shuts down console + * output once the process exits. The agent stops polling for new console + * output, and once all pending data has been written to the output pipe, the + * agent closes the pipe. (At that point, the pipe may still have data in it, + * which the client may read. Once all the data has been read, further reads + * return EOF.) */ +#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ull + +/* After the agent shuts down output, and after all output has been written + * into the pipe(s), exit the agent by closing the console. If there any + * surviving processes still attached to the console, they are killed. + * + * Note: With this flag, an RPC call (e.g. winpty_set_size) issued after the + * agent exits will fail with an I/O or dead-agent error. */ +#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull + +/* All the spawn flags. */ +#define WINPTY_SPAWN_FLAG_MASK (0ull \ + | WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN \ + | WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN \ +) + + + +#endif /* WINPTY_CONSTANTS_H */ diff --git a/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc new file mode 100644 index 0000000000..82d00b2da2 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2011-2016 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 "AgentLocation.h" + +#include <windows.h> + +#include <string> + +#include "../shared/WinptyAssert.h" + +#include "LibWinptyException.h" + +#define AGENT_EXE L"winpty-agent.exe" + +static HMODULE getCurrentModule() { + HMODULE module; + if (!GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast<LPCWSTR>(getCurrentModule), + &module)) { + ASSERT(false && "GetModuleHandleEx failed"); + } + return module; +} + +static std::wstring getModuleFileName(HMODULE module) { + const int bufsize = 4096; + wchar_t path[bufsize]; + int size = GetModuleFileNameW(module, path, bufsize); + ASSERT(size != 0 && size != bufsize); + return std::wstring(path); +} + +static std::wstring dirname(const std::wstring &path) { + std::wstring::size_type pos = path.find_last_of(L"\\/"); + if (pos == std::wstring::npos) { + return L""; + } else { + return path.substr(0, pos); + } +} + +static bool pathExists(const std::wstring &path) { + return GetFileAttributesW(path.c_str()) != 0xFFFFFFFF; +} + +std::wstring findAgentProgram() { + std::wstring progDir = dirname(getModuleFileName(getCurrentModule())); + std::wstring ret = progDir + (L"\\" AGENT_EXE); + if (!pathExists(ret)) { + throw LibWinptyException( + WINPTY_ERROR_AGENT_EXE_MISSING, + (L"agent executable does not exist: '" + ret + L"'").c_str()); + } + return ret; +} diff --git a/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h new file mode 100644 index 0000000000..a96b854cd2 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011-2016 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 LIBWINPTY_AGENT_LOCATION_H +#define LIBWINPTY_AGENT_LOCATION_H + +#include <string> + +std::wstring findAgentProgram(); + +#endif // LIBWINPTY_AGENT_LOCATION_H diff --git a/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h b/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h new file mode 100644 index 0000000000..2274798d23 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016 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 LIB_WINPTY_EXCEPTION_H +#define LIB_WINPTY_EXCEPTION_H + +#include "../include/winpty.h" + +#include "../shared/WinptyException.h" + +#include <memory> +#include <string> + +class LibWinptyException : public WinptyException { +public: + LibWinptyException(winpty_result_t code, const wchar_t *what) : + m_code(code), m_what(std::make_shared<std::wstring>(what)) {} + + winpty_result_t code() const WINPTY_NOEXCEPT { + return m_code; + } + + const wchar_t *what() const WINPTY_NOEXCEPT override { + return m_what->c_str(); + } + + std::shared_ptr<std::wstring> whatSharedStr() const WINPTY_NOEXCEPT { + return m_what; + } + +private: + winpty_result_t m_code; + // Using a shared_ptr ensures that copying the object raises no exception. + std::shared_ptr<std::wstring> m_what; +}; + +#endif // LIB_WINPTY_EXCEPTION_H diff --git a/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h b/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h new file mode 100644 index 0000000000..93e992d5c5 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h @@ -0,0 +1,72 @@ +// Copyright (c) 2016 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 LIBWINPTY_WINPTY_INTERNAL_H +#define LIBWINPTY_WINPTY_INTERNAL_H + +#include <memory> +#include <string> + +#include "../include/winpty.h" + +#include "../shared/Mutex.h" +#include "../shared/OwnedHandle.h" + +// The structures in this header are not intended to be accessed directly by +// client programs. + +struct winpty_error_s { + winpty_result_t code; + const wchar_t *msgStatic; + // Use a pointer to a std::shared_ptr so that the struct remains simple + // enough to statically initialize, for the benefit of static error + // objects like kOutOfMemory. + std::shared_ptr<std::wstring> *msgDynamic; +}; + +struct winpty_config_s { + uint64_t flags = 0; + int cols = 80; + int rows = 25; + int mouseMode = WINPTY_MOUSE_MODE_AUTO; + DWORD timeoutMs = 30000; +}; + +struct winpty_s { + Mutex mutex; + OwnedHandle agentProcess; + OwnedHandle controlPipe; + DWORD agentTimeoutMs = 0; + OwnedHandle ioEvent; + std::wstring spawnDesktopName; + std::wstring coninPipeName; + std::wstring conoutPipeName; + std::wstring conerrPipeName; +}; + +struct winpty_spawn_config_s { + uint64_t winptyFlags = 0; + std::wstring appname; + std::wstring cmdline; + std::wstring cwd; + std::wstring env; +}; + +#endif // LIBWINPTY_WINPTY_INTERNAL_H diff --git a/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk b/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk new file mode 100644 index 0000000000..ba32bad6e6 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk @@ -0,0 +1,46 @@ +# Copyright (c) 2011-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. + +ALL_TARGETS += build/winpty.dll + +$(eval $(call def_mingw_target,libwinpty,-DCOMPILING_WINPTY_DLL)) + +LIBWINPTY_OBJECTS = \ + build/libwinpty/libwinpty/AgentLocation.o \ + build/libwinpty/libwinpty/winpty.o \ + build/libwinpty/shared/BackgroundDesktop.o \ + build/libwinpty/shared/Buffer.o \ + build/libwinpty/shared/DebugClient.o \ + build/libwinpty/shared/GenRandom.o \ + build/libwinpty/shared/OwnedHandle.o \ + build/libwinpty/shared/StringUtil.o \ + build/libwinpty/shared/WindowsSecurity.o \ + build/libwinpty/shared/WindowsVersion.o \ + build/libwinpty/shared/WinptyAssert.o \ + build/libwinpty/shared/WinptyException.o \ + build/libwinpty/shared/WinptyVersion.o + +build/libwinpty/shared/WinptyVersion.o : build/gen/GenVersion.h + +build/winpty.dll : $(LIBWINPTY_OBJECTS) + $(info Linking $@) + @$(MINGW_CXX) $(MINGW_LDFLAGS) -shared -o $@ $^ -Wl,--out-implib,build/winpty.lib + +-include $(LIBWINPTY_OBJECTS:.o=.d) diff --git a/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc b/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc new file mode 100644 index 0000000000..3d977498ef --- /dev/null +++ b/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc @@ -0,0 +1,970 @@ +// Copyright (c) 2011-2016 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 <windows.h> + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <limits> +#include <string> +#include <vector> + +#include "../include/winpty.h" + +#include "../shared/AgentMsg.h" +#include "../shared/BackgroundDesktop.h" +#include "../shared/Buffer.h" +#include "../shared/DebugClient.h" +#include "../shared/GenRandom.h" +#include "../shared/OwnedHandle.h" +#include "../shared/StringBuilder.h" +#include "../shared/StringUtil.h" +#include "../shared/WindowsSecurity.h" +#include "../shared/WindowsVersion.h" +#include "../shared/WinptyAssert.h" +#include "../shared/WinptyException.h" +#include "../shared/WinptyVersion.h" + +#include "AgentLocation.h" +#include "LibWinptyException.h" +#include "WinptyInternal.h" + + + +/***************************************************************************** + * Error handling -- translate C++ exceptions to an optional error object + * output and log the result. */ + +static const winpty_error_s kOutOfMemory = { + WINPTY_ERROR_OUT_OF_MEMORY, + L"Out of memory", + nullptr +}; + +static const winpty_error_s kBadRpcPacket = { + WINPTY_ERROR_UNSPECIFIED, + L"Bad RPC packet", + nullptr +}; + +static const winpty_error_s kUncaughtException = { + WINPTY_ERROR_UNSPECIFIED, + L"Uncaught C++ exception", + nullptr +}; + +/* Gets the error code from the error object. */ +WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err) { + return err != nullptr ? err->code : WINPTY_ERROR_SUCCESS; +} + +/* Returns a textual representation of the error. The string is freed when + * the error is freed. */ +WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err) { + if (err != nullptr) { + if (err->msgStatic != nullptr) { + return err->msgStatic; + } else { + ASSERT(err->msgDynamic != nullptr); + std::wstring *msgPtr = err->msgDynamic->get(); + ASSERT(msgPtr != nullptr); + return msgPtr->c_str(); + } + } else { + return L"Success"; + } +} + +/* Free the error object. Every error returned from the winpty API must be + * freed. */ +WINPTY_API void winpty_error_free(winpty_error_ptr_t err) { + if (err != nullptr && err->msgDynamic != nullptr) { + delete err->msgDynamic; + delete err; + } +} + +static void translateException(winpty_error_ptr_t *&err) { + winpty_error_ptr_t ret = nullptr; + try { + try { + throw; + } catch (const ReadBuffer::DecodeError&) { + ret = const_cast<winpty_error_ptr_t>(&kBadRpcPacket); + } catch (const LibWinptyException &e) { + std::unique_ptr<winpty_error_t> obj(new winpty_error_t); + obj->code = e.code(); + obj->msgStatic = nullptr; + obj->msgDynamic = + new std::shared_ptr<std::wstring>(e.whatSharedStr()); + ret = obj.release(); + } catch (const WinptyException &e) { + std::unique_ptr<winpty_error_t> obj(new winpty_error_t); + std::shared_ptr<std::wstring> msg(new std::wstring(e.what())); + obj->code = WINPTY_ERROR_UNSPECIFIED; + obj->msgStatic = nullptr; + obj->msgDynamic = new std::shared_ptr<std::wstring>(msg); + ret = obj.release(); + } + } catch (const std::bad_alloc&) { + ret = const_cast<winpty_error_ptr_t>(&kOutOfMemory); + } catch (...) { + ret = const_cast<winpty_error_ptr_t>(&kUncaughtException); + } + trace("libwinpty error: code=%u msg='%s'", + static_cast<unsigned>(ret->code), + utf8FromWide(winpty_error_msg(ret)).c_str()); + if (err != nullptr) { + *err = ret; + } else { + winpty_error_free(ret); + } +} + +#define API_TRY \ + if (err != nullptr) { *err = nullptr; } \ + try + +#define API_CATCH(ret) \ + catch (...) { translateException(err); return (ret); } + + + +/***************************************************************************** + * Configuration of a new agent. */ + +WINPTY_API winpty_config_t * +winpty_config_new(UINT64 flags, winpty_error_ptr_t *err /*OPTIONAL*/) { + API_TRY { + ASSERT((flags & WINPTY_FLAG_MASK) == flags); + std::unique_ptr<winpty_config_t> ret(new winpty_config_t); + ret->flags = flags; + return ret.release(); + } API_CATCH(nullptr) +} + +WINPTY_API void winpty_config_free(winpty_config_t *cfg) { + delete cfg; +} + +WINPTY_API void +winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows) { + ASSERT(cfg != nullptr && cols > 0 && rows > 0); + cfg->cols = cols; + cfg->rows = rows; +} + +WINPTY_API void +winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode) { + ASSERT(cfg != nullptr && + mouseMode >= WINPTY_MOUSE_MODE_NONE && + mouseMode <= WINPTY_MOUSE_MODE_FORCE); + cfg->mouseMode = mouseMode; +} + +WINPTY_API void +winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs) { + ASSERT(cfg != nullptr && timeoutMs > 0); + cfg->timeoutMs = timeoutMs; +} + + + +/***************************************************************************** + * Agent I/O. */ + +namespace { + +// Once an I/O operation fails with ERROR_IO_PENDING, the caller *must* wait +// for it to complete, even after calling CancelIo on it! See +// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613. This +// class enforces that requirement. +class PendingIo { + HANDLE m_file; + OVERLAPPED &m_over; + bool m_finished; +public: + // The file handle and OVERLAPPED object must live as long as the PendingIo + // object. + PendingIo(HANDLE file, OVERLAPPED &over) : + m_file(file), m_over(over), m_finished(false) {} + ~PendingIo() { + if (!m_finished) { + // We're not usually that interested in CancelIo's return value. + // In any case, we must not throw an exception in this dtor. + CancelIo(m_file); + waitForCompletion(); + } + } + std::tuple<BOOL, DWORD> waitForCompletion(DWORD &actual) WINPTY_NOEXCEPT { + m_finished = true; + const BOOL success = + GetOverlappedResult(m_file, &m_over, &actual, TRUE); + return std::make_tuple(success, GetLastError()); + } + std::tuple<BOOL, DWORD> waitForCompletion() WINPTY_NOEXCEPT { + DWORD actual = 0; + return waitForCompletion(actual); + } +}; + +} // anonymous namespace + +static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success, + DWORD &lastError, DWORD &actual) { + if (!success && lastError == ERROR_IO_PENDING) { + PendingIo io(wp.controlPipe.get(), over); + const HANDLE waitHandles[2] = { wp.ioEvent.get(), + wp.agentProcess.get() }; + DWORD waitRet = WaitForMultipleObjects( + 2, waitHandles, FALSE, wp.agentTimeoutMs); + if (waitRet != WAIT_OBJECT_0) { + // The I/O is still pending. Cancel it, close the I/O event, and + // throw an exception. + if (waitRet == WAIT_OBJECT_0 + 1) { + throw LibWinptyException(WINPTY_ERROR_AGENT_DIED, L"agent died"); + } else if (waitRet == WAIT_TIMEOUT) { + throw LibWinptyException(WINPTY_ERROR_AGENT_TIMEOUT, + L"agent timed out"); + } else if (waitRet == WAIT_FAILED) { + throwWindowsError(L"WaitForMultipleObjects failed"); + } else { + ASSERT(false && + "unexpected WaitForMultipleObjects return value"); + } + } + std::tie(success, lastError) = io.waitForCompletion(actual); + } +} + +static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success, + DWORD &lastError) { + DWORD actual = 0; + handlePendingIo(wp, over, success, lastError, actual); +} + +static void handleReadWriteErrors(winpty_t &wp, BOOL success, DWORD lastError, + const wchar_t *genericErrMsg) { + if (!success) { + // If the pipe connection is broken after it's been connected, then + // later I/O operations fail with ERROR_BROKEN_PIPE (reads) or + // ERROR_NO_DATA (writes). With Wine, they may also fail with + // ERROR_PIPE_NOT_CONNECTED. See this gist[1]. + // + // [1] https://gist.github.com/rprichard/8dd8ca134b39534b7da2733994aa07ba + if (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_NO_DATA || + lastError == ERROR_PIPE_NOT_CONNECTED) { + throw LibWinptyException(WINPTY_ERROR_LOST_CONNECTION, + L"lost connection to agent"); + } else { + throwWindowsError(genericErrMsg, lastError); + } + } +} + +// Calls ConnectNamedPipe to wait until the agent connects to the control pipe. +static void +connectControlPipe(winpty_t &wp) { + OVERLAPPED over = {}; + over.hEvent = wp.ioEvent.get(); + BOOL success = ConnectNamedPipe(wp.controlPipe.get(), &over); + DWORD lastError = GetLastError(); + handlePendingIo(wp, over, success, lastError); + if (!success && lastError == ERROR_PIPE_CONNECTED) { + success = TRUE; + } + if (!success) { + throwWindowsError(L"ConnectNamedPipe failed", lastError); + } +} + +static void writeData(winpty_t &wp, const void *data, size_t amount) { + // Perform a single pipe write. + DWORD actual = 0; + OVERLAPPED over = {}; + over.hEvent = wp.ioEvent.get(); + BOOL success = WriteFile(wp.controlPipe.get(), data, amount, + &actual, &over); + DWORD lastError = GetLastError(); + if (!success) { + handlePendingIo(wp, over, success, lastError, actual); + handleReadWriteErrors(wp, success, lastError, L"WriteFile failed"); + ASSERT(success); + } + // TODO: Can a partial write actually happen somehow? + ASSERT(actual == amount && "WriteFile wrote fewer bytes than requested"); +} + +static inline WriteBuffer newPacket() { + WriteBuffer packet; + packet.putRawValue<uint64_t>(0); // Reserve space for size. + return packet; +} + +static void writePacket(winpty_t &wp, WriteBuffer &packet) { + const auto &buf = packet.buf(); + packet.replaceRawValue<uint64_t>(0, buf.size()); + writeData(wp, buf.data(), buf.size()); +} + +static size_t readData(winpty_t &wp, void *data, size_t amount) { + DWORD actual = 0; + OVERLAPPED over = {}; + over.hEvent = wp.ioEvent.get(); + BOOL success = ReadFile(wp.controlPipe.get(), data, amount, + &actual, &over); + DWORD lastError = GetLastError(); + if (!success) { + handlePendingIo(wp, over, success, lastError, actual); + handleReadWriteErrors(wp, success, lastError, L"ReadFile failed"); + } + return actual; +} + +static void readAll(winpty_t &wp, void *data, size_t amount) { + while (amount > 0) { + const size_t chunk = readData(wp, data, amount); + ASSERT(chunk <= amount && "readData result is larger than amount"); + data = reinterpret_cast<char*>(data) + chunk; + amount -= chunk; + } +} + +static uint64_t readUInt64(winpty_t &wp) { + uint64_t ret = 0; + readAll(wp, &ret, sizeof(ret)); + return ret; +} + +// Returns a reply packet's payload. +static ReadBuffer readPacket(winpty_t &wp) { + const uint64_t packetSize = readUInt64(wp); + if (packetSize < sizeof(packetSize) || packetSize > SIZE_MAX) { + throwWinptyException(L"Agent RPC error: invalid packet size"); + } + const size_t payloadSize = packetSize - sizeof(packetSize); + std::vector<char> bytes(payloadSize); + readAll(wp, bytes.data(), bytes.size()); + return ReadBuffer(std::move(bytes)); +} + +static OwnedHandle createControlPipe(const std::wstring &name) { + const auto sd = createPipeSecurityDescriptorOwnerFullControl(); + if (!sd) { + throwWinptyException( + L"could not create the control pipe's SECURITY_DESCRIPTOR"); + } + SECURITY_ATTRIBUTES sa = {}; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = sd.get(); + HANDLE ret = CreateNamedPipeW(name.c_str(), + /*dwOpenMode=*/ + PIPE_ACCESS_DUPLEX | + FILE_FLAG_FIRST_PIPE_INSTANCE | + FILE_FLAG_OVERLAPPED, + /*dwPipeMode=*/rejectRemoteClientsPipeFlag(), + /*nMaxInstances=*/1, + /*nOutBufferSize=*/8192, + /*nInBufferSize=*/256, + /*nDefaultTimeOut=*/30000, + &sa); + if (ret == INVALID_HANDLE_VALUE) { + throwWindowsError(L"CreateNamedPipeW failed"); + } + return OwnedHandle(ret); +} + + + +/***************************************************************************** + * Start the agent. */ + +static OwnedHandle createEvent() { + // manual reset, initially unset + HANDLE h = CreateEventW(nullptr, TRUE, FALSE, nullptr); + if (h == nullptr) { + throwWindowsError(L"CreateEventW failed"); + } + return OwnedHandle(h); +} + +// For debugging purposes, provide a way to keep the console on the main window +// station, visible. +static bool shouldShowConsoleWindow() { + char buf[32]; + return GetEnvironmentVariableA("WINPTY_SHOW_CONSOLE", buf, sizeof(buf)) > 0; +} + +static bool shouldCreateBackgroundDesktop(bool &createUsingAgent) { + // Prior to Windows 7, winpty's repeated selection-deselection loop + // prevented the user from interacting with their *visible* console + // windows, unless we placed the console onto a background desktop. + // The SetProcessWindowStation call interferes with the clipboard and + // isn't thread-safe, though[1]. The call should perhaps occur in a + // special agent subprocess. Spawning a process in a background desktop + // also breaks ConEmu, but marking the process SW_HIDE seems to correct + // that[2]. + // + // Windows 7 moved a lot of console handling out of csrss.exe and into + // a per-console conhost.exe process, which may explain why it isn't + // affected. + // + // This is a somewhat risky change, so there are low-level flags to + // assist in debugging if there are issues. + // + // [1] https://github.com/rprichard/winpty/issues/58 + // [2] https://github.com/rprichard/winpty/issues/70 + bool ret = !shouldShowConsoleWindow() && !isAtLeastWindows7(); + const bool force = hasDebugFlag("force_desktop"); + const bool force_spawn = hasDebugFlag("force_desktop_spawn"); + const bool force_curproc = hasDebugFlag("force_desktop_curproc"); + const bool suppress = hasDebugFlag("no_desktop"); + if (force + force_spawn + force_curproc + suppress > 1) { + trace("error: Only one of force_desktop, force_desktop_spawn, " + "force_desktop_curproc, and no_desktop may be set"); + } else if (force) { + ret = true; + } else if (force_spawn) { + ret = true; + createUsingAgent = true; + } else if (force_curproc) { + ret = true; + createUsingAgent = false; + } else if (suppress) { + ret = false; + } + return ret; +} + +static bool shouldSpecifyHideFlag() { + const bool force = hasDebugFlag("force_sw_hide"); + const bool suppress = hasDebugFlag("no_sw_hide"); + bool ret = !shouldShowConsoleWindow(); + if (force && suppress) { + trace("error: Both the force_sw_hide and no_sw_hide flags are set"); + } else if (force) { + ret = true; + } else if (suppress) { + ret = false; + } + return ret; +} + +static OwnedHandle startAgentProcess( + const std::wstring &desktop, + const std::wstring &controlPipeName, + const std::wstring ¶ms, + DWORD creationFlags, + DWORD &agentPid) { + const std::wstring exePath = findAgentProgram(); + const std::wstring cmdline = + (WStringBuilder(256) + << L"\"" << exePath << L"\" " + << controlPipeName << L' ' + << params).str_moved(); + + auto cmdlineV = vectorWithNulFromString(cmdline); + auto desktopV = vectorWithNulFromString(desktop); + + // Start the agent. + STARTUPINFOW sui = {}; + sui.cb = sizeof(sui); + sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data(); + + if (shouldSpecifyHideFlag()) { + sui.dwFlags |= STARTF_USESHOWWINDOW; + sui.wShowWindow = SW_HIDE; + } + PROCESS_INFORMATION pi = {}; + const BOOL success = + CreateProcessW(exePath.c_str(), + cmdlineV.data(), + nullptr, nullptr, + /*bInheritHandles=*/FALSE, + /*dwCreationFlags=*/creationFlags, + nullptr, nullptr, + &sui, &pi); + if (!success) { + const DWORD lastError = GetLastError(); + const auto errStr = + (WStringBuilder(256) + << L"winpty-agent CreateProcess failed: cmdline='" << cmdline + << L"' err=0x" << whexOfInt(lastError)).str_moved(); + throw LibWinptyException( + WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str()); + } + CloseHandle(pi.hThread); + TRACE("Created agent successfully, pid=%u, cmdline=%s", + static_cast<unsigned int>(pi.dwProcessId), + utf8FromWide(cmdline).c_str()); + agentPid = pi.dwProcessId; + return OwnedHandle(pi.hProcess); +} + +static void verifyPipeClientPid(HANDLE serverPipe, DWORD agentPid) { + const auto client = getNamedPipeClientProcessId(serverPipe); + const auto success = std::get<0>(client); + const auto lastError = std::get<2>(client); + if (success == GetNamedPipeClientProcessId_Result::Success) { + const auto clientPid = std::get<1>(client); + if (clientPid != agentPid) { + WStringBuilder errMsg; + errMsg << L"Security check failed: pipe client pid (" << clientPid + << L") does not match agent pid (" << agentPid << L")"; + throwWinptyException(errMsg.c_str()); + } + } else if (success == GetNamedPipeClientProcessId_Result::UnsupportedOs) { + trace("Pipe client PID security check skipped: " + "GetNamedPipeClientProcessId unsupported on this OS version"); + } else { + throwWindowsError(L"GetNamedPipeClientProcessId failed", lastError); + } +} + +static std::unique_ptr<winpty_t> +createAgentSession(const winpty_config_t *cfg, + const std::wstring &desktop, + const std::wstring ¶ms, + DWORD creationFlags) { + std::unique_ptr<winpty_t> wp(new winpty_t); + wp->agentTimeoutMs = cfg->timeoutMs; + wp->ioEvent = createEvent(); + + // Create control server pipe. + const auto pipeName = + L"\\\\.\\pipe\\winpty-control-" + GenRandom().uniqueName(); + wp->controlPipe = createControlPipe(pipeName); + + DWORD agentPid = 0; + wp->agentProcess = startAgentProcess( + desktop, pipeName, params, creationFlags, agentPid); + connectControlPipe(*wp.get()); + verifyPipeClientPid(wp->controlPipe.get(), agentPid); + + return std::move(wp); +} + +namespace { + +class AgentDesktop { +public: + virtual std::wstring name() = 0; + virtual ~AgentDesktop() {} +}; + +class AgentDesktopDirect : public AgentDesktop { +public: + AgentDesktopDirect(BackgroundDesktop &&desktop) : + m_desktop(std::move(desktop)) + { + } + std::wstring name() override { return m_desktop.desktopName(); } +private: + BackgroundDesktop m_desktop; +}; + +class AgentDesktopIndirect : public AgentDesktop { +public: + AgentDesktopIndirect(std::unique_ptr<winpty_t> &&wp, + std::wstring &&desktopName) : + m_wp(std::move(wp)), + m_desktopName(std::move(desktopName)) + { + } + std::wstring name() override { return m_desktopName; } +private: + std::unique_ptr<winpty_t> m_wp; + std::wstring m_desktopName; +}; + +} // anonymous namespace + +std::unique_ptr<AgentDesktop> +setupBackgroundDesktop(const winpty_config_t *cfg) { + bool useDesktopAgent = + !(cfg->flags & WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION); + const bool useDesktop = shouldCreateBackgroundDesktop(useDesktopAgent); + + if (!useDesktop) { + return std::unique_ptr<AgentDesktop>(); + } + + if (useDesktopAgent) { + auto wp = createAgentSession( + cfg, std::wstring(), L"--create-desktop", DETACHED_PROCESS); + + // Read the desktop name. + auto packet = readPacket(*wp.get()); + auto desktopName = packet.getWString(); + packet.assertEof(); + + if (desktopName.empty()) { + return std::unique_ptr<AgentDesktop>(); + } else { + return std::unique_ptr<AgentDesktop>( + new AgentDesktopIndirect(std::move(wp), + std::move(desktopName))); + } + } else { + try { + BackgroundDesktop desktop; + return std::unique_ptr<AgentDesktop>(new AgentDesktopDirect( + std::move(desktop))); + } catch (const WinptyException &e) { + trace("Error: failed to create background desktop, " + "using original desktop instead: %s", + utf8FromWide(e.what()).c_str()); + return std::unique_ptr<AgentDesktop>(); + } + } +} + +WINPTY_API winpty_t * +winpty_open(const winpty_config_t *cfg, + winpty_error_ptr_t *err /*OPTIONAL*/) { + API_TRY { + ASSERT(cfg != nullptr); + dumpWindowsVersion(); + dumpVersionToTrace(); + + // Setup a background desktop for the agent. + auto desktop = setupBackgroundDesktop(cfg); + const auto desktopName = desktop ? desktop->name() : std::wstring(); + + // Start the primary agent session. + const auto params = + (WStringBuilder(128) + << cfg->flags << L' ' + << cfg->mouseMode << L' ' + << cfg->cols << L' ' + << cfg->rows).str_moved(); + auto wp = createAgentSession(cfg, desktopName, params, + CREATE_NEW_CONSOLE); + + // Close handles to the background desktop and restore the original + // window station. This must wait until we know the agent is running + // -- if we close these handles too soon, then the desktop and + // windowstation will be destroyed before the agent can connect with + // them. + // + // If we used a separate agent process to create the desktop, we + // disconnect from that process here, allowing it to exit. + desktop.reset(); + + // If we ran the agent process on a background desktop, then when we + // spawn a child process from the agent, it will need to be explicitly + // placed back onto the original desktop. + if (!desktopName.empty()) { + wp->spawnDesktopName = getCurrentDesktopName(); + } + + // Get the CONIN/CONOUT pipe names. + auto packet = readPacket(*wp.get()); + wp->coninPipeName = packet.getWString(); + wp->conoutPipeName = packet.getWString(); + if (cfg->flags & WINPTY_FLAG_CONERR) { + wp->conerrPipeName = packet.getWString(); + } + packet.assertEof(); + + return wp.release(); + } API_CATCH(nullptr) +} + +WINPTY_API HANDLE winpty_agent_process(winpty_t *wp) { + ASSERT(wp != nullptr); + return wp->agentProcess.get(); +} + + + +/***************************************************************************** + * I/O pipes. */ + +static const wchar_t *cstrFromWStringOrNull(const std::wstring &str) { + try { + return str.c_str(); + } catch (const std::bad_alloc&) { + return nullptr; + } +} + +WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp) { + ASSERT(wp != nullptr); + return cstrFromWStringOrNull(wp->coninPipeName); +} + +WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp) { + ASSERT(wp != nullptr); + return cstrFromWStringOrNull(wp->conoutPipeName); +} + +WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp) { + ASSERT(wp != nullptr); + if (wp->conerrPipeName.empty()) { + return nullptr; + } else { + return cstrFromWStringOrNull(wp->conerrPipeName); + } +} + + + +/***************************************************************************** + * winpty agent RPC calls. */ + +namespace { + +// Close the control pipe if something goes wrong with the pipe communication, +// which could leave the control pipe in an inconsistent state. +class RpcOperation { +public: + RpcOperation(winpty_t &wp) : m_wp(wp) { + if (m_wp.controlPipe.get() == nullptr) { + throwWinptyException(L"Agent shutdown due to RPC failure"); + } + } + ~RpcOperation() { + if (!m_success) { + trace("~RpcOperation: Closing control pipe"); + m_wp.controlPipe.dispose(true); + } + } + void success() { m_success = true; } +private: + winpty_t &m_wp; + bool m_success = false; +}; + +} // anonymous namespace + + + +/***************************************************************************** + * winpty agent RPC call: process creation. */ + +// Return a std::wstring containing every character of the environment block. +// Typically, the block is non-empty, so the std::wstring returned ends with +// two NUL terminators. (These two terminators are counted in size(), so +// calling c_str() produces a triply-terminated string.) +static std::wstring wstringFromEnvBlock(const wchar_t *env) { + std::wstring envStr; + if (env != NULL) { + const wchar_t *p = env; + while (*p != L'\0') { + p += wcslen(p) + 1; + } + p++; + envStr.assign(env, p); + + // Assuming the environment was non-empty, envStr now ends with two NUL + // terminators. + // + // If the environment were empty, though, then envStr would only be + // singly terminated, but the MSDN documentation thinks an env block is + // always doubly-terminated, so add an extra NUL just in case it + // matters. + const auto envStrSz = envStr.size(); + if (envStrSz == 1) { + ASSERT(envStr[0] == L'\0'); + envStr.push_back(L'\0'); + } else { + ASSERT(envStrSz >= 3); + ASSERT(envStr[envStrSz - 3] != L'\0'); + ASSERT(envStr[envStrSz - 2] == L'\0'); + ASSERT(envStr[envStrSz - 1] == L'\0'); + } + } + return envStr; +} + +WINPTY_API winpty_spawn_config_t * +winpty_spawn_config_new(UINT64 winptyFlags, + LPCWSTR appname /*OPTIONAL*/, + LPCWSTR cmdline /*OPTIONAL*/, + LPCWSTR cwd /*OPTIONAL*/, + LPCWSTR env /*OPTIONAL*/, + winpty_error_ptr_t *err /*OPTIONAL*/) { + API_TRY { + ASSERT((winptyFlags & WINPTY_SPAWN_FLAG_MASK) == winptyFlags); + std::unique_ptr<winpty_spawn_config_t> cfg(new winpty_spawn_config_t); + cfg->winptyFlags = winptyFlags; + if (appname != nullptr) { cfg->appname = appname; } + if (cmdline != nullptr) { cfg->cmdline = cmdline; } + if (cwd != nullptr) { cfg->cwd = cwd; } + if (env != nullptr) { cfg->env = wstringFromEnvBlock(env); } + return cfg.release(); + } API_CATCH(nullptr) +} + +WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg) { + delete cfg; +} + +// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it +// back to 64-bits. See the MSDN article, "Interprocess Communication Between +// 32-bit and 64-bit Applications". +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx +static inline HANDLE handleFromInt64(int64_t i) { + return reinterpret_cast<HANDLE>(static_cast<intptr_t>(i)); +} + +// Given a process and a handle in that process, duplicate the handle into the +// current process and close it in the originating process. +static inline OwnedHandle stealHandle(HANDLE process, HANDLE handle) { + HANDLE result = nullptr; + if (!DuplicateHandle(process, handle, + GetCurrentProcess(), + &result, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + throwWindowsError(L"DuplicateHandle of process handle"); + } + return OwnedHandle(result); +} + +WINPTY_API BOOL +winpty_spawn(winpty_t *wp, + const winpty_spawn_config_t *cfg, + HANDLE *process_handle /*OPTIONAL*/, + HANDLE *thread_handle /*OPTIONAL*/, + DWORD *create_process_error /*OPTIONAL*/, + winpty_error_ptr_t *err /*OPTIONAL*/) { + API_TRY { + ASSERT(wp != nullptr && cfg != nullptr); + + if (process_handle != nullptr) { *process_handle = nullptr; } + if (thread_handle != nullptr) { *thread_handle = nullptr; } + if (create_process_error != nullptr) { *create_process_error = 0; } + + LockGuard<Mutex> lock(wp->mutex); + RpcOperation rpc(*wp); + + // Send spawn request. + auto packet = newPacket(); + packet.putInt32(AgentMsg::StartProcess); + packet.putInt64(cfg->winptyFlags); + packet.putInt32(process_handle != nullptr); + packet.putInt32(thread_handle != nullptr); + packet.putWString(cfg->appname); + packet.putWString(cfg->cmdline); + packet.putWString(cfg->cwd); + packet.putWString(cfg->env); + packet.putWString(wp->spawnDesktopName); + writePacket(*wp, packet); + + // Receive reply. + auto reply = readPacket(*wp); + const auto result = static_cast<StartProcessResult>(reply.getInt32()); + if (result == StartProcessResult::CreateProcessFailed) { + const DWORD lastError = reply.getInt32(); + reply.assertEof(); + if (create_process_error != nullptr) { + *create_process_error = lastError; + } + rpc.success(); + throw LibWinptyException(WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED, + L"CreateProcess failed"); + } else if (result == StartProcessResult::ProcessCreated) { + const HANDLE remoteProcess = handleFromInt64(reply.getInt64()); + const HANDLE remoteThread = handleFromInt64(reply.getInt64()); + reply.assertEof(); + OwnedHandle localProcess; + OwnedHandle localThread; + if (remoteProcess != nullptr) { + localProcess = + stealHandle(wp->agentProcess.get(), remoteProcess); + } + if (remoteThread != nullptr) { + localThread = + stealHandle(wp->agentProcess.get(), remoteThread); + } + if (process_handle != nullptr) { + *process_handle = localProcess.release(); + } + if (thread_handle != nullptr) { + *thread_handle = localThread.release(); + } + rpc.success(); + } else { + throwWinptyException( + L"Agent RPC error: invalid StartProcessResult"); + } + return TRUE; + } API_CATCH(FALSE) +} + + + +/***************************************************************************** + * winpty agent RPC calls: everything else */ + +WINPTY_API BOOL +winpty_set_size(winpty_t *wp, int cols, int rows, + winpty_error_ptr_t *err /*OPTIONAL*/) { + API_TRY { + ASSERT(wp != nullptr && cols > 0 && rows > 0); + LockGuard<Mutex> lock(wp->mutex); + RpcOperation rpc(*wp); + auto packet = newPacket(); + packet.putInt32(AgentMsg::SetSize); + packet.putInt32(cols); + packet.putInt32(rows); + writePacket(*wp, packet); + readPacket(*wp).assertEof(); + rpc.success(); + return TRUE; + } API_CATCH(FALSE) +} + +WINPTY_API int +winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount, + winpty_error_ptr_t *err /*OPTIONAL*/) { + API_TRY { + ASSERT(wp != nullptr); + ASSERT(processList != nullptr); + LockGuard<Mutex> lock(wp->mutex); + RpcOperation rpc(*wp); + auto packet = newPacket(); + packet.putInt32(AgentMsg::GetConsoleProcessList); + writePacket(*wp, packet); + auto reply = readPacket(*wp); + + auto actualProcessCount = reply.getInt32(); + + if (actualProcessCount <= processCount) { + for (auto i = 0; i < actualProcessCount; i++) { + processList[i] = reply.getInt32(); + } + } + + reply.assertEof(); + rpc.success(); + return actualProcessCount; + } API_CATCH(0) +} + +WINPTY_API void winpty_free(winpty_t *wp) { + // At least in principle, CloseHandle can fail, so this deletion can + // fail. It won't throw an exception, but maybe there's an error that + // should be propagated? + delete wp; +} diff --git a/src/libs/3rdparty/winpty/src/shared/AgentMsg.h b/src/libs/3rdparty/winpty/src/shared/AgentMsg.h new file mode 100644 index 0000000000..ab60c6b961 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/AgentMsg.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011-2012 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 WINPTY_SHARED_AGENT_MSG_H +#define WINPTY_SHARED_AGENT_MSG_H + +struct AgentMsg +{ + enum Type { + StartProcess, + SetSize, + GetConsoleProcessList, + }; +}; + +enum class StartProcessResult { + CreateProcessFailed, + ProcessCreated, +}; + +#endif // WINPTY_SHARED_AGENT_MSG_H diff --git a/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc new file mode 100644 index 0000000000..1bea7e53dd --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2011-2016 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 "BackgroundDesktop.h" + +#include <memory> + +#include "DebugClient.h" +#include "StringUtil.h" +#include "WinptyException.h" + +namespace { + +static std::wstring getObjectName(HANDLE object) { + BOOL success; + DWORD lengthNeeded = 0; + GetUserObjectInformationW(object, UOI_NAME, + nullptr, 0, + &lengthNeeded); + ASSERT(lengthNeeded % sizeof(wchar_t) == 0); + std::unique_ptr<wchar_t[]> tmp( + new wchar_t[lengthNeeded / sizeof(wchar_t)]); + success = GetUserObjectInformationW(object, UOI_NAME, + tmp.get(), lengthNeeded, + nullptr); + if (!success) { + throwWindowsError(L"GetUserObjectInformationW failed"); + } + return std::wstring(tmp.get()); +} + +static std::wstring getDesktopName(HWINSTA winsta, HDESK desk) { + return getObjectName(winsta) + L"\\" + getObjectName(desk); +} + +} // anonymous namespace + +// Get a non-interactive window station for the agent. +// TODO: review security w.r.t. windowstation and desktop. +BackgroundDesktop::BackgroundDesktop() { + try { + m_originalStation = GetProcessWindowStation(); + if (m_originalStation == nullptr) { + throwWindowsError( + L"BackgroundDesktop ctor: " + L"GetProcessWindowStation returned NULL"); + } + m_newStation = + CreateWindowStationW(nullptr, 0, WINSTA_ALL_ACCESS, nullptr); + if (m_newStation == nullptr) { + throwWindowsError( + L"BackgroundDesktop ctor: CreateWindowStationW returned NULL"); + } + if (!SetProcessWindowStation(m_newStation)) { + throwWindowsError( + L"BackgroundDesktop ctor: SetProcessWindowStation failed"); + } + m_newDesktop = CreateDesktopW( + L"Default", nullptr, nullptr, 0, GENERIC_ALL, nullptr); + if (m_newDesktop == nullptr) { + throwWindowsError( + L"BackgroundDesktop ctor: CreateDesktopW failed"); + } + m_newDesktopName = getDesktopName(m_newStation, m_newDesktop); + TRACE("Created background desktop: %s", + utf8FromWide(m_newDesktopName).c_str()); + } catch (...) { + dispose(); + throw; + } +} + +void BackgroundDesktop::dispose() WINPTY_NOEXCEPT { + if (m_originalStation != nullptr) { + SetProcessWindowStation(m_originalStation); + m_originalStation = nullptr; + } + if (m_newDesktop != nullptr) { + CloseDesktop(m_newDesktop); + m_newDesktop = nullptr; + } + if (m_newStation != nullptr) { + CloseWindowStation(m_newStation); + m_newStation = nullptr; + } +} + +std::wstring getCurrentDesktopName() { + // MSDN says that the handles returned by GetProcessWindowStation and + // GetThreadDesktop do not need to be passed to CloseWindowStation and + // CloseDesktop, respectively. + const HWINSTA winsta = GetProcessWindowStation(); + if (winsta == nullptr) { + throwWindowsError( + L"getCurrentDesktopName: " + L"GetProcessWindowStation returned NULL"); + } + const HDESK desk = GetThreadDesktop(GetCurrentThreadId()); + if (desk == nullptr) { + throwWindowsError( + L"getCurrentDesktopName: " + L"GetThreadDesktop returned NULL"); + } + return getDesktopName(winsta, desk); +} diff --git a/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h new file mode 100644 index 0000000000..c692e57dc4 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h @@ -0,0 +1,73 @@ +// Copyright (c) 2011-2016 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 WINPTY_SHARED_BACKGROUND_DESKTOP_H +#define WINPTY_SHARED_BACKGROUND_DESKTOP_H + +#include <windows.h> + +#include <string> + +#include "WinptyException.h" + +class BackgroundDesktop { +public: + BackgroundDesktop(); + ~BackgroundDesktop() { dispose(); } + void dispose() WINPTY_NOEXCEPT; + const std::wstring &desktopName() const { return m_newDesktopName; } + + BackgroundDesktop(const BackgroundDesktop &other) = delete; + BackgroundDesktop &operator=(const BackgroundDesktop &other) = delete; + + // We can't default the move constructor and assignment operator with + // MSVC 2013. We *could* if we required at least MSVC 2015 to build. + + BackgroundDesktop(BackgroundDesktop &&other) : + m_originalStation(other.m_originalStation), + m_newStation(other.m_newStation), + m_newDesktop(other.m_newDesktop), + m_newDesktopName(std::move(other.m_newDesktopName)) { + other.m_originalStation = nullptr; + other.m_newStation = nullptr; + other.m_newDesktop = nullptr; + } + BackgroundDesktop &operator=(BackgroundDesktop &&other) { + dispose(); + m_originalStation = other.m_originalStation; + m_newStation = other.m_newStation; + m_newDesktop = other.m_newDesktop; + m_newDesktopName = std::move(other.m_newDesktopName); + other.m_originalStation = nullptr; + other.m_newStation = nullptr; + other.m_newDesktop = nullptr; + return *this; + } + +private: + HWINSTA m_originalStation = nullptr; + HWINSTA m_newStation = nullptr; + HDESK m_newDesktop = nullptr; + std::wstring m_newDesktopName; +}; + +std::wstring getCurrentDesktopName(); + +#endif // WINPTY_SHARED_BACKGROUND_DESKTOP_H diff --git a/src/libs/3rdparty/winpty/src/shared/Buffer.cc b/src/libs/3rdparty/winpty/src/shared/Buffer.cc new file mode 100644 index 0000000000..158a629d56 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/Buffer.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2011-2016 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 "Buffer.h" + +#include <stdint.h> + +#include "DebugClient.h" +#include "WinptyAssert.h" + +// Define the READ_BUFFER_CHECK() macro. It *must* evaluate its condition, +// exactly once. +#define READ_BUFFER_CHECK(cond) \ + do { \ + if (!(cond)) { \ + trace("decode error: %s", #cond); \ + throw DecodeError(); \ + } \ + } while (false) + +enum class Piece : uint8_t { Int32, Int64, WString }; + +void WriteBuffer::putRawData(const void *data, size_t len) { + const auto p = reinterpret_cast<const char*>(data); + m_buf.insert(m_buf.end(), p, p + len); +} + +void WriteBuffer::replaceRawData(size_t pos, const void *data, size_t len) { + ASSERT(pos <= m_buf.size() && len <= m_buf.size() - pos); + const auto p = reinterpret_cast<const char*>(data); + std::copy(p, p + len, &m_buf[pos]); +} + +void WriteBuffer::putInt32(int32_t i) { + putRawValue(Piece::Int32); + putRawValue(i); +} + +void WriteBuffer::putInt64(int64_t i) { + putRawValue(Piece::Int64); + putRawValue(i); +} + +// len is in characters, excluding NUL, i.e. the number of wchar_t elements +void WriteBuffer::putWString(const wchar_t *str, size_t len) { + putRawValue(Piece::WString); + putRawValue(static_cast<uint64_t>(len)); + putRawData(str, sizeof(wchar_t) * len); +} + +void ReadBuffer::getRawData(void *data, size_t len) { + ASSERT(m_off <= m_buf.size()); + READ_BUFFER_CHECK(len <= m_buf.size() - m_off); + const char *const inp = &m_buf[m_off]; + std::copy(inp, inp + len, reinterpret_cast<char*>(data)); + m_off += len; +} + +int32_t ReadBuffer::getInt32() { + READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::Int32); + return getRawValue<int32_t>(); +} + +int64_t ReadBuffer::getInt64() { + READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::Int64); + return getRawValue<int64_t>(); +} + +std::wstring ReadBuffer::getWString() { + READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::WString); + const uint64_t charLen = getRawValue<uint64_t>(); + READ_BUFFER_CHECK(charLen <= SIZE_MAX / sizeof(wchar_t)); + // To be strictly conforming, we can't use the convenient wstring + // constructor, because the string in m_buf mightn't be aligned. + std::wstring ret; + if (charLen > 0) { + const size_t byteLen = charLen * sizeof(wchar_t); + ret.resize(charLen); + getRawData(&ret[0], byteLen); + } + return ret; +} + +void ReadBuffer::assertEof() { + READ_BUFFER_CHECK(m_off == m_buf.size()); +} diff --git a/src/libs/3rdparty/winpty/src/shared/Buffer.h b/src/libs/3rdparty/winpty/src/shared/Buffer.h new file mode 100644 index 0000000000..c2dd382e5b --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/Buffer.h @@ -0,0 +1,102 @@ +// Copyright (c) 2011-2016 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 WINPTY_SHARED_BUFFER_H +#define WINPTY_SHARED_BUFFER_H + +#include <stdint.h> +#include <string.h> + +#include <algorithm> +#include <utility> +#include <vector> +#include <string> + +#include "WinptyException.h" + +class WriteBuffer { +private: + std::vector<char> m_buf; + +public: + WriteBuffer() {} + + template <typename T> void putRawValue(const T &t) { + putRawData(&t, sizeof(t)); + } + template <typename T> void replaceRawValue(size_t pos, const T &t) { + replaceRawData(pos, &t, sizeof(t)); + } + + void putRawData(const void *data, size_t len); + void replaceRawData(size_t pos, const void *data, size_t len); + void putInt32(int32_t i); + void putInt64(int64_t i); + void putWString(const wchar_t *str, size_t len); + void putWString(const wchar_t *str) { putWString(str, wcslen(str)); } + void putWString(const std::wstring &str) { putWString(str.data(), str.size()); } + std::vector<char> &buf() { return m_buf; } + + // MSVC 2013 does not generate these automatically, so help it out. + WriteBuffer(WriteBuffer &&other) : m_buf(std::move(other.m_buf)) {} + WriteBuffer &operator=(WriteBuffer &&other) { + m_buf = std::move(other.m_buf); + return *this; + } +}; + +class ReadBuffer { +public: + class DecodeError : public WinptyException { + virtual const wchar_t *what() const WINPTY_NOEXCEPT override { + return L"DecodeError: RPC message decoding error"; + } + }; + +private: + std::vector<char> m_buf; + size_t m_off = 0; + +public: + explicit ReadBuffer(std::vector<char> &&buf) : m_buf(std::move(buf)) {} + + template <typename T> T getRawValue() { + T ret = {}; + getRawData(&ret, sizeof(ret)); + return ret; + } + + void getRawData(void *data, size_t len); + int32_t getInt32(); + int64_t getInt64(); + std::wstring getWString(); + void assertEof(); + + // MSVC 2013 does not generate these automatically, so help it out. + ReadBuffer(ReadBuffer &&other) : + m_buf(std::move(other.m_buf)), m_off(other.m_off) {} + ReadBuffer &operator=(ReadBuffer &&other) { + m_buf = std::move(other.m_buf); + m_off = other.m_off; + return *this; + } +}; + +#endif // WINPTY_SHARED_BUFFER_H diff --git a/src/libs/3rdparty/winpty/src/shared/DebugClient.cc b/src/libs/3rdparty/winpty/src/shared/DebugClient.cc new file mode 100644 index 0000000000..bafe0c8954 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/DebugClient.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2011-2012 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 "DebugClient.h" + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <algorithm> +#include <string> + +#include "winpty_snprintf.h" + +const wchar_t *const kPipeName = L"\\\\.\\pipe\\DebugServer"; + +void *volatile g_debugConfig; + +namespace { + +// It would be easy to accidentally trample on the Windows LastError value +// by adding logging/debugging code. Ensure that can't happen by saving and +// restoring the value. This saving and restoring doesn't happen along the +// fast path. +class PreserveLastError { +public: + PreserveLastError() : m_lastError(GetLastError()) {} + ~PreserveLastError() { SetLastError(m_lastError); } +private: + DWORD m_lastError; +}; + +} // anonymous namespace + +static void sendToDebugServer(const char *message) +{ + HANDLE tracePipe = INVALID_HANDLE_VALUE; + + do { + // The default impersonation level is SECURITY_IMPERSONATION, which allows + // a sufficiently authorized named pipe server to impersonate the client. + // There's no need for impersonation in this debugging system, so reduce + // the impersonation level to SECURITY_IDENTIFICATION, which allows a + // server to merely identify us. + tracePipe = CreateFileW( + kPipeName, + GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, + NULL); + } while (tracePipe == INVALID_HANDLE_VALUE && + GetLastError() == ERROR_PIPE_BUSY && + WaitNamedPipeW(kPipeName, NMPWAIT_WAIT_FOREVER)); + + if (tracePipe != INVALID_HANDLE_VALUE) { + DWORD newMode = PIPE_READMODE_MESSAGE; + SetNamedPipeHandleState(tracePipe, &newMode, NULL, NULL); + char response[16]; + DWORD actual = 0; + TransactNamedPipe(tracePipe, + const_cast<char*>(message), strlen(message), + response, sizeof(response), &actual, NULL); + CloseHandle(tracePipe); + } +} + +// Get the current UTC time as milliseconds from the epoch (ignoring leap +// seconds). Use the Unix epoch for consistency with DebugClient.py. There +// are 134774 days between 1601-01-01 (the Win32 epoch) and 1970-01-01 (the +// Unix epoch). +static long long unixTimeMillis() +{ + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + long long msTime = (((long long)fileTime.dwHighDateTime << 32) + + fileTime.dwLowDateTime) / 10000; + return msTime - 134774LL * 24 * 3600 * 1000; +} + +static const char *getDebugConfig() +{ + if (g_debugConfig == NULL) { + PreserveLastError preserve; + const int bufSize = 256; + char buf[bufSize]; + DWORD actualSize = + GetEnvironmentVariableA("WINPTY_DEBUG", buf, bufSize); + if (actualSize == 0 || actualSize >= static_cast<DWORD>(bufSize)) { + buf[0] = '\0'; + } + const size_t len = strlen(buf) + 1; + char *newConfig = new char[len]; + std::copy(buf, buf + len, newConfig); + void *oldValue = InterlockedCompareExchangePointer( + &g_debugConfig, newConfig, NULL); + if (oldValue != NULL) { + delete [] newConfig; + } + } + return static_cast<const char*>(g_debugConfig); +} + +bool isTracingEnabled() +{ + static bool disabled, enabled; + if (disabled) { + return false; + } else if (enabled) { + return true; + } else { + // Recognize WINPTY_DEBUG=1 for backwards compatibility. + PreserveLastError preserve; + bool value = hasDebugFlag("trace") || hasDebugFlag("1"); + disabled = !value; + enabled = value; + return value; + } +} + +bool hasDebugFlag(const char *flag) +{ + if (strchr(flag, ',') != NULL) { + trace("INTERNAL ERROR: hasDebugFlag flag has comma: '%s'", flag); + abort(); + } + const char *const configCStr = getDebugConfig(); + if (configCStr[0] == '\0') { + return false; + } + PreserveLastError preserve; + std::string config(configCStr); + std::string flagStr(flag); + config = "," + config + ","; + flagStr = "," + flagStr + ","; + return config.find(flagStr) != std::string::npos; +} + +void trace(const char *format, ...) +{ + if (!isTracingEnabled()) + return; + + PreserveLastError preserve; + char message[1024]; + + va_list ap; + va_start(ap, format); + winpty_vsnprintf(message, format, ap); + message[sizeof(message) - 1] = '\0'; + va_end(ap); + + const int currentTime = (int)(unixTimeMillis() % (100000 * 1000)); + + char moduleName[1024]; + moduleName[0] = '\0'; + GetModuleFileNameA(NULL, moduleName, sizeof(moduleName)); + const char *baseName = strrchr(moduleName, '\\'); + baseName = (baseName != NULL) ? baseName + 1 : moduleName; + + char fullMessage[1024]; + winpty_snprintf(fullMessage, + "[%05d.%03d %s,p%04d,t%04d]: %s", + currentTime / 1000, currentTime % 1000, + baseName, (int)GetCurrentProcessId(), (int)GetCurrentThreadId(), + message); + fullMessage[sizeof(fullMessage) - 1] = '\0'; + + sendToDebugServer(fullMessage); +} diff --git a/src/libs/3rdparty/winpty/src/shared/DebugClient.h b/src/libs/3rdparty/winpty/src/shared/DebugClient.h new file mode 100644 index 0000000000..b126071130 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/DebugClient.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011-2012 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 DEBUGCLIENT_H +#define DEBUGCLIENT_H + +#include "winpty_snprintf.h" + +bool isTracingEnabled(); +bool hasDebugFlag(const char *flag); +void trace(const char *format, ...) WINPTY_SNPRINTF_FORMAT(1, 2); + +// This macro calls trace without evaluating the arguments. +#define TRACE(format, ...) \ + do { \ + if (isTracingEnabled()) { \ + trace((format), ## __VA_ARGS__); \ + } \ + } while (false) + +#endif // DEBUGCLIENT_H diff --git a/src/libs/3rdparty/winpty/src/shared/GenRandom.cc b/src/libs/3rdparty/winpty/src/shared/GenRandom.cc new file mode 100644 index 0000000000..6d7920643a --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/GenRandom.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2016 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 "GenRandom.h" + +#include <stdint.h> +#include <string.h> + +#include "DebugClient.h" +#include "StringBuilder.h" + +static volatile LONG g_pipeCounter; + +GenRandom::GenRandom() : m_advapi32(L"advapi32.dll") { + // First try to use the pseudo-documented RtlGenRandom function from + // advapi32.dll. Creating a CryptoAPI context is slow, and RtlGenRandom + // avoids the overhead. It's documented in this blog post[1] and on + // MSDN[2] with a disclaimer about future breakage. This technique is + // apparently built-in into the MSVC CRT, though, for the rand_s function, + // so perhaps it is stable enough. + // + // [1] http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx + // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx + // + // Both RtlGenRandom and the Crypto API functions exist in XP and up. + m_rtlGenRandom = reinterpret_cast<RtlGenRandom_t*>( + m_advapi32.proc("SystemFunction036")); + // The OsModule class logs an error message if the proc is nullptr. + if (m_rtlGenRandom != nullptr) { + return; + } + + // Fall back to the crypto API. + m_cryptProvIsValid = + CryptAcquireContext(&m_cryptProv, nullptr, nullptr, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) != 0; + if (!m_cryptProvIsValid) { + trace("GenRandom: CryptAcquireContext failed: %u", + static_cast<unsigned>(GetLastError())); + } +} + +GenRandom::~GenRandom() { + if (m_cryptProvIsValid) { + CryptReleaseContext(m_cryptProv, 0); + } +} + +// Returns false if the context is invalid or the generation fails. +bool GenRandom::fillBuffer(void *buffer, size_t size) { + memset(buffer, 0, size); + bool success = false; + if (m_rtlGenRandom != nullptr) { + success = m_rtlGenRandom(buffer, size) != 0; + if (!success) { + trace("GenRandom: RtlGenRandom/SystemFunction036 failed: %u", + static_cast<unsigned>(GetLastError())); + } + } else if (m_cryptProvIsValid) { + success = + CryptGenRandom(m_cryptProv, size, + reinterpret_cast<BYTE*>(buffer)) != 0; + if (!success) { + trace("GenRandom: CryptGenRandom failed, size=%d, lasterror=%u", + static_cast<int>(size), + static_cast<unsigned>(GetLastError())); + } + } + return success; +} + +// Returns an empty string if either of CryptAcquireContext or CryptGenRandom +// fail. +std::string GenRandom::randomBytes(size_t numBytes) { + std::string ret(numBytes, '\0'); + if (!fillBuffer(&ret[0], numBytes)) { + return std::string(); + } + return ret; +} + +std::wstring GenRandom::randomHexString(size_t numBytes) { + const std::string bytes = randomBytes(numBytes); + std::wstring ret(bytes.size() * 2, L'\0'); + for (size_t i = 0; i < bytes.size(); ++i) { + static const wchar_t hex[] = L"0123456789abcdef"; + ret[i * 2] = hex[static_cast<uint8_t>(bytes[i]) >> 4]; + ret[i * 2 + 1] = hex[static_cast<uint8_t>(bytes[i]) & 0xF]; + } + return ret; +} + +// Returns a 64-bit value representing the number of 100-nanosecond intervals +// since January 1, 1601. +static uint64_t systemTimeAsUInt64() { + FILETIME monotonicTime = {}; + GetSystemTimeAsFileTime(&monotonicTime); + return (static_cast<uint64_t>(monotonicTime.dwHighDateTime) << 32) | + static_cast<uint64_t>(monotonicTime.dwLowDateTime); +} + +// Generates a unique and hard-to-guess case-insensitive string suitable for +// use in a pipe filename or a Windows object name. +std::wstring GenRandom::uniqueName() { + // First include enough information to avoid collisions assuming + // cooperative software. This code assumes that a process won't die and + // be replaced with a recycled PID within a single GetSystemTimeAsFileTime + // interval. + WStringBuilder sb(64); + sb << GetCurrentProcessId() + << L'-' << InterlockedIncrement(&g_pipeCounter) + << L'-' << whexOfInt(systemTimeAsUInt64()); + // It isn't clear to me how the crypto APIs would fail. It *probably* + // doesn't matter that much anyway? In principle, a predictable pipe name + // is subject to a local denial-of-service attack. + auto random = randomHexString(16); + if (!random.empty()) { + sb << L'-' << random; + } + return sb.str_moved(); +} diff --git a/src/libs/3rdparty/winpty/src/shared/GenRandom.h b/src/libs/3rdparty/winpty/src/shared/GenRandom.h new file mode 100644 index 0000000000..746cb1ecf7 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/GenRandom.h @@ -0,0 +1,55 @@ +// Copyright (c) 2016 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 WINPTY_GEN_RANDOM_H +#define WINPTY_GEN_RANDOM_H + +// The original MinGW requires that we include wincrypt.h. With MinGW-w64 and +// MSVC, including windows.h is sufficient. +#include <windows.h> +#include <wincrypt.h> + +#include <string> + +#include "OsModule.h" + +class GenRandom { + typedef BOOLEAN WINAPI RtlGenRandom_t(PVOID, ULONG); + + OsModule m_advapi32; + RtlGenRandom_t *m_rtlGenRandom = nullptr; + bool m_cryptProvIsValid = false; + HCRYPTPROV m_cryptProv = 0; + +public: + GenRandom(); + ~GenRandom(); + bool fillBuffer(void *buffer, size_t size); + std::string randomBytes(size_t numBytes); + std::wstring randomHexString(size_t numBytes); + std::wstring uniqueName(); + + // Return true if the crypto context was successfully initialized. + bool valid() const { + return m_rtlGenRandom != nullptr || m_cryptProvIsValid; + } +}; + +#endif // WINPTY_GEN_RANDOM_H diff --git a/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat b/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat new file mode 100644 index 0000000000..a9f8e9cef0 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat @@ -0,0 +1,13 @@ +@echo off + +REM -- Echo the git commit hash. If git isn't available for some reason, +REM -- output nothing instead. + +git rev-parse HEAD >NUL 2>NUL && ( + git rev-parse HEAD +) || ( + echo none +) + +REM -- Set ERRORLEVEL to 0 using this cryptic syntax. +(call ) diff --git a/src/libs/3rdparty/winpty/src/shared/Mutex.h b/src/libs/3rdparty/winpty/src/shared/Mutex.h new file mode 100644 index 0000000000..98215365ad --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/Mutex.h @@ -0,0 +1,54 @@ +// 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. + +// Recent 4.x MinGW and MinGW-w64 gcc compilers lack std::mutex and +// std::lock_guard. I have a 5.2.0 MinGW-w64 compiler packaged through MSYS2 +// that *is* new enough, but that's one compiler against several deficient +// ones. Wrap CRITICAL_SECTION instead. + +#ifndef WINPTY_SHARED_MUTEX_H +#define WINPTY_SHARED_MUTEX_H + +#include <windows.h> + +class Mutex { + CRITICAL_SECTION m_mutex; +public: + Mutex() { InitializeCriticalSection(&m_mutex); } + ~Mutex() { DeleteCriticalSection(&m_mutex); } + void lock() { EnterCriticalSection(&m_mutex); } + void unlock() { LeaveCriticalSection(&m_mutex); } + + Mutex(const Mutex &other) = delete; + Mutex &operator=(const Mutex &other) = delete; +}; + +template <typename T> +class LockGuard { + T &m_lock; +public: + LockGuard(T &lock) : m_lock(lock) { m_lock.lock(); } + ~LockGuard() { m_lock.unlock(); } + + LockGuard(const LockGuard &other) = delete; + LockGuard &operator=(const LockGuard &other) = delete; +}; + +#endif // WINPTY_SHARED_MUTEX_H diff --git a/src/libs/3rdparty/winpty/src/shared/OsModule.h b/src/libs/3rdparty/winpty/src/shared/OsModule.h new file mode 100644 index 0000000000..9713fa2b2d --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/OsModule.h @@ -0,0 +1,63 @@ +// 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 WINPTY_SHARED_OS_MODULE_H +#define WINPTY_SHARED_OS_MODULE_H + +#include <windows.h> + +#include <string> + +#include "DebugClient.h" +#include "WinptyAssert.h" +#include "WinptyException.h" + +class OsModule { + HMODULE m_module; +public: + enum class LoadErrorBehavior { Abort, Throw }; + OsModule(const wchar_t *fileName, + LoadErrorBehavior behavior=LoadErrorBehavior::Abort) { + m_module = LoadLibraryW(fileName); + if (behavior == LoadErrorBehavior::Abort) { + ASSERT(m_module != NULL); + } else { + if (m_module == nullptr) { + const auto err = GetLastError(); + throwWindowsError( + (L"LoadLibraryW error: " + std::wstring(fileName)).c_str(), + err); + } + } + } + ~OsModule() { + FreeLibrary(m_module); + } + HMODULE handle() const { return m_module; } + FARPROC proc(const char *funcName) { + FARPROC ret = GetProcAddress(m_module, funcName); + if (ret == NULL) { + trace("GetProcAddress: %s is missing", funcName); + } + return ret; + } +}; + +#endif // WINPTY_SHARED_OS_MODULE_H diff --git a/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc new file mode 100644 index 0000000000..7b173536e6 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2016 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 "OwnedHandle.h" + +#include "DebugClient.h" +#include "WinptyException.h" + +void OwnedHandle::dispose(bool nothrow) { + if (m_h != nullptr && m_h != INVALID_HANDLE_VALUE) { + if (!CloseHandle(m_h)) { + trace("CloseHandle(%p) failed", m_h); + if (!nothrow) { + throwWindowsError(L"CloseHandle failed"); + } + } + } + m_h = nullptr; +} diff --git a/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h new file mode 100644 index 0000000000..70a8d6163a --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h @@ -0,0 +1,45 @@ +// Copyright (c) 2016 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 WINPTY_SHARED_OWNED_HANDLE_H +#define WINPTY_SHARED_OWNED_HANDLE_H + +#include <windows.h> + +class OwnedHandle { + HANDLE m_h; +public: + OwnedHandle() : m_h(nullptr) {} + explicit OwnedHandle(HANDLE h) : m_h(h) {} + ~OwnedHandle() { dispose(true); } + void dispose(bool nothrow=false); + HANDLE get() const { return m_h; } + HANDLE release() { HANDLE ret = m_h; m_h = nullptr; return ret; } + OwnedHandle(const OwnedHandle &other) = delete; + OwnedHandle(OwnedHandle &&other) : m_h(other.release()) {} + OwnedHandle &operator=(const OwnedHandle &other) = delete; + OwnedHandle &operator=(OwnedHandle &&other) { + dispose(); + m_h = other.release(); + return *this; + } +}; + +#endif // WINPTY_SHARED_OWNED_HANDLE_H diff --git a/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h b/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h new file mode 100644 index 0000000000..7d9b8f8b4a --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h @@ -0,0 +1,43 @@ +// Copyright (c) 2016 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 WINPTY_PRECOMPILED_HEADER_H +#define WINPTY_PRECOMPILED_HEADER_H + +#include <windows.h> + +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <array> +#include <limits> +#include <memory> +#include <new> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +#endif // WINPTY_PRECOMPILED_HEADER_H diff --git a/src/libs/3rdparty/winpty/src/shared/StringBuilder.h b/src/libs/3rdparty/winpty/src/shared/StringBuilder.h new file mode 100644 index 0000000000..f3155bdd29 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/StringBuilder.h @@ -0,0 +1,227 @@ +// Copyright (c) 2016 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. + +// Efficient integer->string conversion and string concatenation. The +// hexadecimal conversion may optionally have leading zeros. Other ways to +// convert integers to strings in C++ suffer these drawbacks: +// +// * std::stringstream: Inefficient, even more so than stdio. +// +// * std::to_string: No hexadecimal output, tends to use heap allocation, not +// supported on Cygwin. +// +// * stdio routines: Requires parsing a format string (inefficient). The +// caller *must* know how large the content is for correctness. The +// string-printf functions are extremely inconsistent on Windows. In +// particular, 64-bit integers, wide strings, and return values are +// problem areas. +// +// StringBuilderTest.cc is a standalone program that tests this header. + +#ifndef WINPTY_STRING_BUILDER_H +#define WINPTY_STRING_BUILDER_H + +#include <array> +#include <string> +#include <type_traits> + +#ifdef STRING_BUILDER_TESTING +#include <assert.h> +#define STRING_BUILDER_CHECK(cond) assert(cond) +#else +#define STRING_BUILDER_CHECK(cond) +#endif // STRING_BUILDER_TESTING + +#include "WinptyAssert.h" + +template <typename C, size_t sz> +struct ValueString { + std::array<C, sz> m_array; + size_t m_offset; + size_t m_size; + + const C *c_str() const { return m_array.data() + m_offset; } + const C *data() const { return m_array.data() + m_offset; } + size_t size() const { return m_size; } + std::basic_string<C> str() const { + return std::basic_string<C>(data(), m_size); + } +}; + +#ifdef _MSC_VER +// Disable an MSVC /SDL error that forbids unsigned negation. Signed negation +// invokes undefined behavior for INTxx_MIN, so unsigned negation is simpler to +// reason about. (We assume twos-complement in any case.) +#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) \ + ( \ + __pragma(warning(push)) \ + __pragma(warning(disable:4146)) \ + (x) \ + __pragma(warning(pop)) \ + ) +#else +#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) (x) +#endif + +// Formats an integer as decimal without leading zeros. +template <typename C, typename I> +ValueString<C, sizeof(I) * 3 + 1 + 1> gdecOfInt(const I value) { + typedef typename std::make_unsigned<I>::type U; + auto unsValue = static_cast<U>(value); + const bool isNegative = (value < 0); + if (isNegative) { + unsValue = STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(-unsValue); + } + decltype(gdecOfInt<C, I>(value)) out; + auto &arr = out.m_array; + C *const endp = arr.data() + arr.size(); + C *outp = endp; + *(--outp) = '\0'; + STRING_BUILDER_CHECK(outp >= arr.data()); + do { + const int digit = unsValue % 10; + unsValue /= 10; + *(--outp) = '0' + digit; + STRING_BUILDER_CHECK(outp >= arr.data()); + } while (unsValue != 0); + if (isNegative) { + *(--outp) = '-'; + STRING_BUILDER_CHECK(outp >= arr.data()); + } + out.m_offset = outp - arr.data(); + out.m_size = endp - outp - 1; + return out; +} + +template <typename I> decltype(gdecOfInt<char, I>(0)) decOfInt(I i) { + return gdecOfInt<char>(i); +} + +template <typename I> decltype(gdecOfInt<wchar_t, I>(0)) wdecOfInt(I i) { + return gdecOfInt<wchar_t>(i); +} + +// Formats an integer as hexadecimal, with or without leading zeros. +template <typename C, bool leadingZeros=false, typename I> +ValueString<C, sizeof(I) * 2 + 1> ghexOfInt(const I value) { + typedef typename std::make_unsigned<I>::type U; + const auto unsValue = static_cast<U>(value); + static const C hex[16] = {'0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f'}; + decltype(ghexOfInt<C, leadingZeros, I>(value)) out; + auto &arr = out.m_array; + C *outp = arr.data(); + int inIndex = 0; + int shift = sizeof(I) * 8 - 4; + const int len = sizeof(I) * 2; + if (!leadingZeros) { + for (; inIndex < len - 1; ++inIndex, shift -= 4) { + STRING_BUILDER_CHECK(shift >= 0 && shift < sizeof(unsValue) * 8); + const int digit = (unsValue >> shift) & 0xF; + if (digit != 0) { + break; + } + } + } + for (; inIndex < len; ++inIndex, shift -= 4) { + const int digit = (unsValue >> shift) & 0xF; + *(outp++) = hex[digit]; + STRING_BUILDER_CHECK(outp <= arr.data() + arr.size()); + } + *(outp++) = '\0'; + STRING_BUILDER_CHECK(outp <= arr.data() + arr.size()); + out.m_offset = 0; + out.m_size = outp - arr.data() - 1; + return out; +} + +template <bool leadingZeros=false, typename I> +decltype(ghexOfInt<char, leadingZeros, I>(0)) hexOfInt(I i) { + return ghexOfInt<char, leadingZeros, I>(i); +} + +template <bool leadingZeros=false, typename I> +decltype(ghexOfInt<wchar_t, leadingZeros, I>(0)) whexOfInt(I i) { + return ghexOfInt<wchar_t, leadingZeros, I>(i); +} + +template <typename C> +class GStringBuilder { +public: + typedef std::basic_string<C> StringType; + + GStringBuilder() {} + GStringBuilder(size_t capacity) { + m_out.reserve(capacity); + } + + GStringBuilder &operator<<(C ch) { m_out.push_back(ch); return *this; } + GStringBuilder &operator<<(const C *str) { m_out.append(str); return *this; } + GStringBuilder &operator<<(const StringType &str) { m_out.append(str); return *this; } + + template <size_t sz> + GStringBuilder &operator<<(const ValueString<C, sz> &str) { + m_out.append(str.data(), str.size()); + return *this; + } + +private: + // Forbid output of char/wchar_t for GStringBuilder if the type doesn't + // exactly match the builder element type. The code still allows + // signed char and unsigned char, but I'm a little worried about what + // happens if a user tries to output int8_t or uint8_t. + template <typename P> + typename std::enable_if< + (std::is_same<P, char>::value || std::is_same<P, wchar_t>::value) && + !std::is_same<C, P>::value, GStringBuilder&>::type + operator<<(P ch) { + ASSERT(false && "Method was not supposed to be reachable."); + return *this; + } + +public: + GStringBuilder &operator<<(short i) { return *this << gdecOfInt<C>(i); } + GStringBuilder &operator<<(unsigned short i) { return *this << gdecOfInt<C>(i); } + GStringBuilder &operator<<(int i) { return *this << gdecOfInt<C>(i); } + GStringBuilder &operator<<(unsigned int i) { return *this << gdecOfInt<C>(i); } + GStringBuilder &operator<<(long i) { return *this << gdecOfInt<C>(i); } + GStringBuilder &operator<<(unsigned long i) { return *this << gdecOfInt<C>(i); } + GStringBuilder &operator<<(long long i) { return *this << gdecOfInt<C>(i); } + GStringBuilder &operator<<(unsigned long long i) { return *this << gdecOfInt<C>(i); } + + GStringBuilder &operator<<(const void *p) { + m_out.push_back(static_cast<C>('0')); + m_out.push_back(static_cast<C>('x')); + *this << ghexOfInt<C>(reinterpret_cast<uintptr_t>(p)); + return *this; + } + + StringType str() { return m_out; } + StringType str_moved() { return std::move(m_out); } + const C *c_str() const { return m_out.c_str(); } + +private: + StringType m_out; +}; + +typedef GStringBuilder<char> StringBuilder; +typedef GStringBuilder<wchar_t> WStringBuilder; + +#endif // WINPTY_STRING_BUILDER_H diff --git a/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc b/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc new file mode 100644 index 0000000000..e6c2d3138c --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2016 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. + +#define STRING_BUILDER_TESTING + +#include "StringBuilder.h" + +#include <stdio.h> +#include <string.h> + +#include <iomanip> +#include <sstream> + +void display(const std::string &str) { fprintf(stderr, "%s", str.c_str()); } +void display(const std::wstring &str) { fprintf(stderr, "%ls", str.c_str()); } + +#define CHECK_EQ(x, y) \ + do { \ + const auto xval = (x); \ + const auto yval = (y); \ + if (xval != yval) { \ + fprintf(stderr, "error: %s:%d: %s != %s: ", \ + __FILE__, __LINE__, #x, #y); \ + display(xval); \ + fprintf(stderr, " != "); \ + display(yval); \ + fprintf(stderr, "\n"); \ + } \ + } while(0) + +template <typename C, typename I> +std::basic_string<C> decOfIntSS(const I value) { + // std::to_string and std::to_wstring are missing in Cygwin as of this + // writing (early 2016). + std::basic_stringstream<C> ss; + ss << +value; // We must promote char to print it as an integer. + return ss.str(); +} + + +template <typename C, bool leadingZeros=false, typename I> +std::basic_string<C> hexOfIntSS(const I value) { + typedef typename std::make_unsigned<I>::type U; + const unsigned long long u64Value = value & static_cast<U>(~0); + std::basic_stringstream<C> ss; + if (leadingZeros) { + ss << std::setfill(static_cast<C>('0')) << std::setw(sizeof(I) * 2); + } + ss << std::hex << u64Value; + return ss.str(); +} + +template <typename I> +void testValue(I value) { + CHECK_EQ(decOfInt(value).str(), (decOfIntSS<char>(value))); + CHECK_EQ(wdecOfInt(value).str(), (decOfIntSS<wchar_t>(value))); + CHECK_EQ((hexOfInt<false>(value).str()), (hexOfIntSS<char, false>(value))); + CHECK_EQ((hexOfInt<true>(value).str()), (hexOfIntSS<char, true>(value))); + CHECK_EQ((whexOfInt<false>(value).str()), (hexOfIntSS<wchar_t, false>(value))); + CHECK_EQ((whexOfInt<true>(value).str()), (hexOfIntSS<wchar_t, true>(value))); +} + +template <typename I> +void testType() { + typedef typename std::make_unsigned<I>::type U; + const U quarter = static_cast<U>(1) << (sizeof(U) * 8 - 2); + for (unsigned quarterIndex = 0; quarterIndex < 4; ++quarterIndex) { + for (int offset = -18; offset <= 18; ++offset) { + const I value = quarter * quarterIndex + static_cast<U>(offset); + testValue(value); + } + } + testValue(static_cast<I>(42)); + testValue(static_cast<I>(123456)); + testValue(static_cast<I>(0xdeadfacecafebeefull)); +} + +int main() { + testType<char>(); + + testType<signed char>(); + testType<signed short>(); + testType<signed int>(); + testType<signed long>(); + testType<signed long long>(); + + testType<unsigned char>(); + testType<unsigned short>(); + testType<unsigned int>(); + testType<unsigned long>(); + testType<unsigned long long>(); + + StringBuilder() << static_cast<const void*>("TEST"); + WStringBuilder() << static_cast<const void*>("TEST"); + + fprintf(stderr, "All tests completed!\n"); +} diff --git a/src/libs/3rdparty/winpty/src/shared/StringUtil.cc b/src/libs/3rdparty/winpty/src/shared/StringUtil.cc new file mode 100644 index 0000000000..3a85a3ec94 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/StringUtil.cc @@ -0,0 +1,55 @@ +// 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 "StringUtil.h" + +#include <windows.h> + +#include "WinptyAssert.h" + +// Workaround. MinGW (from mingw.org) does not have wcsnlen. MinGW-w64 *does* +// have wcsnlen, but use this function for consistency. +size_t winpty_wcsnlen(const wchar_t *s, size_t maxlen) { + ASSERT(s != NULL); + for (size_t i = 0; i < maxlen; ++i) { + if (s[i] == L'\0') { + return i; + } + } + return maxlen; +} + +std::string utf8FromWide(const std::wstring &input) { + int mblen = WideCharToMultiByte( + CP_UTF8, 0, + input.data(), input.size(), + NULL, 0, NULL, NULL); + if (mblen <= 0) { + return std::string(); + } + std::vector<char> tmp(mblen); + int mblen2 = WideCharToMultiByte( + CP_UTF8, 0, + input.data(), input.size(), + tmp.data(), tmp.size(), + NULL, NULL); + ASSERT(mblen2 == mblen); + return std::string(tmp.data(), tmp.size()); +} diff --git a/src/libs/3rdparty/winpty/src/shared/StringUtil.h b/src/libs/3rdparty/winpty/src/shared/StringUtil.h new file mode 100644 index 0000000000..e4bf3c9121 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/StringUtil.h @@ -0,0 +1,80 @@ +// 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 WINPTY_SHARED_STRING_UTIL_H +#define WINPTY_SHARED_STRING_UTIL_H + +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <algorithm> +#include <string> +#include <vector> + +#include "WinptyAssert.h" + +size_t winpty_wcsnlen(const wchar_t *s, size_t maxlen); +std::string utf8FromWide(const std::wstring &input); + +// Return a vector containing each character in the string. +template <typename T> +std::vector<T> vectorFromString(const std::basic_string<T> &str) { + return std::vector<T>(str.begin(), str.end()); +} + +// Return a vector containing each character in the string, followed by a +// NUL terminator. +template <typename T> +std::vector<T> vectorWithNulFromString(const std::basic_string<T> &str) { + std::vector<T> ret; + ret.reserve(str.size() + 1); + ret.insert(ret.begin(), str.begin(), str.end()); + ret.push_back('\0'); + return ret; +} + +// A safer(?) version of wcsncpy that is accepted by MSVC's /SDL mode. +template <size_t N> +wchar_t *winpty_wcsncpy(wchar_t (&d)[N], const wchar_t *s) { + ASSERT(s != nullptr); + size_t i = 0; + for (; i < N; ++i) { + if (s[i] == L'\0') { + break; + } + d[i] = s[i]; + } + for (; i < N; ++i) { + d[i] = L'\0'; + } + return d; +} + +// Like wcsncpy, but ensure that the destination buffer is NUL-terminated. +template <size_t N> +wchar_t *winpty_wcsncpy_nul(wchar_t (&d)[N], const wchar_t *s) { + static_assert(N > 0, "array cannot be 0-size"); + winpty_wcsncpy(d, s); + d[N - 1] = L'\0'; + return d; +} + +#endif // WINPTY_SHARED_STRING_UTIL_H diff --git a/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h b/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h new file mode 100644 index 0000000000..716a027fcb --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h @@ -0,0 +1,63 @@ +// 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. + +// Convenience header library for using the high-resolution performance counter +// to measure how long some process takes. + +#ifndef TIME_MEASUREMENT_H +#define TIME_MEASUREMENT_H + +#include <windows.h> +#include <assert.h> +#include <stdint.h> + +class TimeMeasurement { +public: + TimeMeasurement() { + static double freq = static_cast<double>(getFrequency()); + m_freq = freq; + m_start = value(); + } + + double elapsed() { + uint64_t elapsedTicks = value() - m_start; + return static_cast<double>(elapsedTicks) / m_freq; + } + +private: + uint64_t getFrequency() { + LARGE_INTEGER freq; + BOOL success = QueryPerformanceFrequency(&freq); + assert(success && "QueryPerformanceFrequency failed"); + return freq.QuadPart; + } + + uint64_t value() { + LARGE_INTEGER ret; + BOOL success = QueryPerformanceCounter(&ret); + assert(success && "QueryPerformanceCounter failed"); + return ret.QuadPart; + } + + uint64_t m_start; + double m_freq; +}; + +#endif // TIME_MEASUREMENT_H diff --git a/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h b/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h new file mode 100644 index 0000000000..39dfa62ec9 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h @@ -0,0 +1,45 @@ +// 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 UNIX_CTRL_CHARS_H +#define UNIX_CTRL_CHARS_H + +inline char decodeUnixCtrlChar(char ch) { + const char ctrlKeys[] = { + /* 0x00 */ '@', /* 0x01 */ 'A', /* 0x02 */ 'B', /* 0x03 */ 'C', + /* 0x04 */ 'D', /* 0x05 */ 'E', /* 0x06 */ 'F', /* 0x07 */ 'G', + /* 0x08 */ 'H', /* 0x09 */ 'I', /* 0x0A */ 'J', /* 0x0B */ 'K', + /* 0x0C */ 'L', /* 0x0D */ 'M', /* 0x0E */ 'N', /* 0x0F */ 'O', + /* 0x10 */ 'P', /* 0x11 */ 'Q', /* 0x12 */ 'R', /* 0x13 */ 'S', + /* 0x14 */ 'T', /* 0x15 */ 'U', /* 0x16 */ 'V', /* 0x17 */ 'W', + /* 0x18 */ 'X', /* 0x19 */ 'Y', /* 0x1A */ 'Z', /* 0x1B */ '[', + /* 0x1C */ '\\', /* 0x1D */ ']', /* 0x1E */ '^', /* 0x1F */ '_', + }; + unsigned char uch = ch; + if (uch < 32) { + return ctrlKeys[uch]; + } else if (uch == 127) { + return '?'; + } else { + return '\0'; + } +} + +#endif // UNIX_CTRL_CHARS_H diff --git a/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat b/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat new file mode 100644 index 0000000000..ea2a7d64ed --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat @@ -0,0 +1,20 @@ +@echo off + +rem -- Echo the git commit hash. If git isn't available for some reason, +rem -- output nothing instead. + +mkdir ..\gen 2>nul + +set /p VERSION=<..\..\VERSION.txt +set COMMIT=%1 + +echo // AUTO-GENERATED BY %0 %*>..\gen\GenVersion.h +echo const char GenVersion_Version[] = "%VERSION%";>>..\gen\GenVersion.h +echo const char GenVersion_Commit[] = "%COMMIT%";>>..\gen\GenVersion.h + +rem -- The winpty.gyp file expects the script to output the include directory, +rem -- relative to src. +echo gen + +rem -- Set ERRORLEVEL to 0 using this cryptic syntax. +(call ) diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc new file mode 100644 index 0000000000..711a8637c8 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc @@ -0,0 +1,460 @@ +// Copyright (c) 2016 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 "WindowsSecurity.h" + +#include <array> + +#include "DebugClient.h" +#include "OsModule.h" +#include "OwnedHandle.h" +#include "StringBuilder.h" +#include "WindowsVersion.h" +#include "WinptyAssert.h" +#include "WinptyException.h" + +namespace { + +struct LocalFreer { + void operator()(void *ptr) { + if (ptr != nullptr) { + LocalFree(reinterpret_cast<HLOCAL>(ptr)); + } + } +}; + +typedef std::unique_ptr<void, LocalFreer> PointerLocal; + +template <typename T> +SecurityItem<T> localItem(typename T::type v) { + typedef typename T::type P; + struct Impl : SecurityItem<T>::Impl { + P m_v; + Impl(P v) : m_v(v) {} + virtual ~Impl() { + LocalFree(reinterpret_cast<HLOCAL>(m_v)); + } + }; + return SecurityItem<T>(v, std::unique_ptr<Impl>(new Impl { v })); +} + +Sid allocatedSid(PSID v) { + struct Impl : Sid::Impl { + PSID m_v; + Impl(PSID v) : m_v(v) {} + virtual ~Impl() { + if (m_v != nullptr) { + FreeSid(m_v); + } + } + }; + return Sid(v, std::unique_ptr<Impl>(new Impl { v })); +} + +} // anonymous namespace + +// Returns a handle to the thread's effective security token. If the thread +// is impersonating another user, its token is returned, and otherwise, the +// process' security token is opened. The handle is opened with TOKEN_QUERY. +static OwnedHandle openSecurityTokenForQuery() { + HANDLE token = nullptr; + // It is unclear to me whether OpenAsSelf matters for winpty, or what the + // most appropriate value is. + if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, + /*OpenAsSelf=*/FALSE, &token)) { + if (GetLastError() != ERROR_NO_TOKEN) { + throwWindowsError(L"OpenThreadToken failed"); + } + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { + throwWindowsError(L"OpenProcessToken failed"); + } + } + ASSERT(token != nullptr && + "OpenThreadToken/OpenProcessToken token is NULL"); + return OwnedHandle(token); +} + +// Returns the TokenOwner of the thread's effective security token. +Sid getOwnerSid() { + struct Impl : Sid::Impl { + std::unique_ptr<char[]> buffer; + }; + + OwnedHandle token = openSecurityTokenForQuery(); + DWORD actual = 0; + BOOL success; + success = GetTokenInformation(token.get(), TokenOwner, + nullptr, 0, &actual); + if (success) { + throwWinptyException(L"getOwnerSid: GetTokenInformation: " + L"expected ERROR_INSUFFICIENT_BUFFER"); + } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + throwWindowsError(L"getOwnerSid: GetTokenInformation: " + L"expected ERROR_INSUFFICIENT_BUFFER"); + } + std::unique_ptr<Impl> impl(new Impl); + impl->buffer = std::unique_ptr<char[]>(new char[actual]); + success = GetTokenInformation(token.get(), TokenOwner, + impl->buffer.get(), actual, &actual); + if (!success) { + throwWindowsError(L"getOwnerSid: GetTokenInformation"); + } + TOKEN_OWNER tmp; + ASSERT(actual >= sizeof(tmp)); + std::copy( + impl->buffer.get(), + impl->buffer.get() + sizeof(tmp), + reinterpret_cast<char*>(&tmp)); + return Sid(tmp.Owner, std::move(impl)); +} + +Sid wellKnownSid( + const wchar_t *debuggingName, + SID_IDENTIFIER_AUTHORITY authority, + BYTE authorityCount, + DWORD subAuthority0/*=0*/, + DWORD subAuthority1/*=0*/) { + PSID psid = nullptr; + if (!AllocateAndInitializeSid(&authority, authorityCount, + subAuthority0, + subAuthority1, + 0, 0, 0, 0, 0, 0, + &psid)) { + const auto err = GetLastError(); + const auto msg = + std::wstring(L"wellKnownSid: error getting ") + + debuggingName + L" SID"; + throwWindowsError(msg.c_str(), err); + } + return allocatedSid(psid); +} + +Sid builtinAdminsSid() { + // S-1-5-32-544 + SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; + return wellKnownSid(L"BUILTIN\\Administrators group", + authority, 2, + SECURITY_BUILTIN_DOMAIN_RID, // 32 + DOMAIN_ALIAS_RID_ADMINS); // 544 +} + +Sid localSystemSid() { + // S-1-5-18 + SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; + return wellKnownSid(L"LocalSystem account", + authority, 1, + SECURITY_LOCAL_SYSTEM_RID); // 18 +} + +Sid everyoneSid() { + // S-1-1-0 + SID_IDENTIFIER_AUTHORITY authority = { SECURITY_WORLD_SID_AUTHORITY }; + return wellKnownSid(L"Everyone account", + authority, 1, + SECURITY_WORLD_RID); // 0 +} + +static SecurityDescriptor finishSecurityDescriptor( + size_t daclEntryCount, + EXPLICIT_ACCESSW *daclEntries, + Acl &outAcl) { + { + PACL aclRaw = nullptr; + DWORD aclError = + SetEntriesInAclW(daclEntryCount, + daclEntries, + nullptr, &aclRaw); + if (aclError != ERROR_SUCCESS) { + WStringBuilder sb(64); + sb << L"finishSecurityDescriptor: " + << L"SetEntriesInAcl failed: " << aclError; + throwWinptyException(sb.c_str()); + } + outAcl = localItem<AclTag>(aclRaw); + } + + const PSECURITY_DESCRIPTOR sdRaw = + reinterpret_cast<PSECURITY_DESCRIPTOR>( + LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH)); + if (sdRaw == nullptr) { + throwWinptyException(L"finishSecurityDescriptor: LocalAlloc failed"); + } + SecurityDescriptor sd = localItem<SecurityDescriptorTag>(sdRaw); + if (!InitializeSecurityDescriptor(sdRaw, SECURITY_DESCRIPTOR_REVISION)) { + throwWindowsError( + L"finishSecurityDescriptor: InitializeSecurityDescriptor"); + } + if (!SetSecurityDescriptorDacl(sdRaw, TRUE, outAcl.get(), FALSE)) { + throwWindowsError( + L"finishSecurityDescriptor: SetSecurityDescriptorDacl"); + } + + return std::move(sd); +} + +// Create a security descriptor that grants full control to the local system +// account, built-in administrators, and the owner. +SecurityDescriptor +createPipeSecurityDescriptorOwnerFullControl() { + + struct Impl : SecurityDescriptor::Impl { + Sid localSystem; + Sid builtinAdmins; + Sid owner; + std::array<EXPLICIT_ACCESSW, 3> daclEntries = {}; + Acl dacl; + SecurityDescriptor value; + }; + + std::unique_ptr<Impl> impl(new Impl); + impl->localSystem = localSystemSid(); + impl->builtinAdmins = builtinAdminsSid(); + impl->owner = getOwnerSid(); + + for (auto &ea : impl->daclEntries) { + ea.grfAccessPermissions = GENERIC_ALL; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + } + impl->daclEntries[0].Trustee.ptstrName = + reinterpret_cast<LPWSTR>(impl->localSystem.get()); + impl->daclEntries[1].Trustee.ptstrName = + reinterpret_cast<LPWSTR>(impl->builtinAdmins.get()); + impl->daclEntries[2].Trustee.ptstrName = + reinterpret_cast<LPWSTR>(impl->owner.get()); + + impl->value = finishSecurityDescriptor( + impl->daclEntries.size(), + impl->daclEntries.data(), + impl->dacl); + + const auto retValue = impl->value.get(); + return SecurityDescriptor(retValue, std::move(impl)); +} + +SecurityDescriptor +createPipeSecurityDescriptorOwnerFullControlEveryoneWrite() { + + struct Impl : SecurityDescriptor::Impl { + Sid localSystem; + Sid builtinAdmins; + Sid owner; + Sid everyone; + std::array<EXPLICIT_ACCESSW, 4> daclEntries = {}; + Acl dacl; + SecurityDescriptor value; + }; + + std::unique_ptr<Impl> impl(new Impl); + impl->localSystem = localSystemSid(); + impl->builtinAdmins = builtinAdminsSid(); + impl->owner = getOwnerSid(); + impl->everyone = everyoneSid(); + + for (auto &ea : impl->daclEntries) { + ea.grfAccessPermissions = GENERIC_ALL; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + } + impl->daclEntries[0].Trustee.ptstrName = + reinterpret_cast<LPWSTR>(impl->localSystem.get()); + impl->daclEntries[1].Trustee.ptstrName = + reinterpret_cast<LPWSTR>(impl->builtinAdmins.get()); + impl->daclEntries[2].Trustee.ptstrName = + reinterpret_cast<LPWSTR>(impl->owner.get()); + impl->daclEntries[3].Trustee.ptstrName = + reinterpret_cast<LPWSTR>(impl->everyone.get()); + // Avoid using FILE_GENERIC_WRITE because it includes FILE_APPEND_DATA, + // which is equal to FILE_CREATE_PIPE_INSTANCE. Instead, include all the + // flags that comprise FILE_GENERIC_WRITE, except for the one. + impl->daclEntries[3].grfAccessPermissions = + FILE_GENERIC_READ | + FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA | + STANDARD_RIGHTS_WRITE | SYNCHRONIZE; + + impl->value = finishSecurityDescriptor( + impl->daclEntries.size(), + impl->daclEntries.data(), + impl->dacl); + + const auto retValue = impl->value.get(); + return SecurityDescriptor(retValue, std::move(impl)); +} + +SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle) { + PACL dacl = nullptr; + PSECURITY_DESCRIPTOR sd = nullptr; + const DWORD errCode = GetSecurityInfo(handle, SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION, + nullptr, nullptr, &dacl, nullptr, &sd); + if (errCode != ERROR_SUCCESS) { + throwWindowsError(L"GetSecurityInfo failed"); + } + return localItem<SecurityDescriptorTag>(sd); +} + +// The (SID/SD)<->string conversion APIs are useful for testing/debugging, so +// create convenient accessor functions for them. They're too slow for +// ordinary use. The APIs exist in XP and up, but the MinGW headers only +// declare the SID<->string APIs, not the SD APIs. MinGW also gets the +// prototype wrong for ConvertStringSidToSidW (LPWSTR instead of LPCWSTR) and +// requires WINVER to be defined. MSVC and MinGW-w64 get everything right, but +// for consistency, use LoadLibrary/GetProcAddress for all four APIs. + +typedef BOOL WINAPI ConvertStringSidToSidW_t( + LPCWSTR StringSid, + PSID *Sid); + +typedef BOOL WINAPI ConvertSidToStringSidW_t( + PSID Sid, + LPWSTR *StringSid); + +typedef BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW_t( + LPCWSTR StringSecurityDescriptor, + DWORD StringSDRevision, + PSECURITY_DESCRIPTOR *SecurityDescriptor, + PULONG SecurityDescriptorSize); + +typedef BOOL WINAPI ConvertSecurityDescriptorToStringSecurityDescriptorW_t( + PSECURITY_DESCRIPTOR SecurityDescriptor, + DWORD RequestedStringSDRevision, + SECURITY_INFORMATION SecurityInformation, + LPWSTR *StringSecurityDescriptor, + PULONG StringSecurityDescriptorLen); + +#define GET_MODULE_PROC(mod, funcName) \ + const auto p##funcName = \ + reinterpret_cast<funcName##_t*>( \ + mod.proc(#funcName)); \ + if (p##funcName == nullptr) { \ + throwWinptyException( \ + L"" L ## #funcName L" API is missing from ADVAPI32.DLL"); \ + } + +const DWORD kSDDL_REVISION_1 = 1; + +std::wstring sidToString(PSID sid) { + OsModule advapi32(L"advapi32.dll"); + GET_MODULE_PROC(advapi32, ConvertSidToStringSidW); + wchar_t *sidString = NULL; + BOOL success = pConvertSidToStringSidW(sid, &sidString); + if (!success) { + throwWindowsError(L"ConvertSidToStringSidW failed"); + } + PointerLocal freer(sidString); + return std::wstring(sidString); +} + +Sid stringToSid(const std::wstring &str) { + // Cast the string from const wchar_t* to LPWSTR because the function is + // incorrectly prototyped in the MinGW sddl.h header. The API does not + // modify the string -- it is correctly prototyped as taking LPCWSTR in + // MinGW-w64, MSVC, and MSDN. + OsModule advapi32(L"advapi32.dll"); + GET_MODULE_PROC(advapi32, ConvertStringSidToSidW); + PSID psid = nullptr; + BOOL success = pConvertStringSidToSidW(const_cast<LPWSTR>(str.c_str()), + &psid); + if (!success) { + const auto err = GetLastError(); + throwWindowsError( + (std::wstring(L"ConvertStringSidToSidW failed on \"") + + str + L'"').c_str(), + err); + } + return localItem<SidTag>(psid); +} + +SecurityDescriptor stringToSd(const std::wstring &str) { + OsModule advapi32(L"advapi32.dll"); + GET_MODULE_PROC(advapi32, ConvertStringSecurityDescriptorToSecurityDescriptorW); + PSECURITY_DESCRIPTOR desc = nullptr; + if (!pConvertStringSecurityDescriptorToSecurityDescriptorW( + str.c_str(), kSDDL_REVISION_1, &desc, nullptr)) { + const auto err = GetLastError(); + throwWindowsError( + (std::wstring(L"ConvertStringSecurityDescriptorToSecurityDescriptorW failed on \"") + + str + L'"').c_str(), + err); + } + return localItem<SecurityDescriptorTag>(desc); +} + +std::wstring sdToString(PSECURITY_DESCRIPTOR sd) { + OsModule advapi32(L"advapi32.dll"); + GET_MODULE_PROC(advapi32, ConvertSecurityDescriptorToStringSecurityDescriptorW); + wchar_t *sdString = nullptr; + if (!pConvertSecurityDescriptorToStringSecurityDescriptorW( + sd, + kSDDL_REVISION_1, + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION, + &sdString, + nullptr)) { + throwWindowsError( + L"ConvertSecurityDescriptorToStringSecurityDescriptor failed"); + } + PointerLocal freer(sdString); + return std::wstring(sdString); +} + +// Vista added a useful flag to CreateNamedPipe, PIPE_REJECT_REMOTE_CLIENTS, +// that rejects remote connections. Return this flag on Vista, or return 0 +// otherwise. +DWORD rejectRemoteClientsPipeFlag() { + if (isAtLeastWindowsVista()) { + // MinGW lacks this flag; MinGW-w64 has it. + const DWORD kPIPE_REJECT_REMOTE_CLIENTS = 8; + return kPIPE_REJECT_REMOTE_CLIENTS; + } else { + trace("Omitting PIPE_REJECT_REMOTE_CLIENTS on pre-Vista OS"); + return 0; + } +} + +typedef BOOL WINAPI GetNamedPipeClientProcessId_t( + HANDLE Pipe, + PULONG ClientProcessId); + +std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD> +getNamedPipeClientProcessId(HANDLE serverPipe) { + OsModule kernel32(L"kernel32.dll"); + const auto pGetNamedPipeClientProcessId = + reinterpret_cast<GetNamedPipeClientProcessId_t*>( + kernel32.proc("GetNamedPipeClientProcessId")); + if (pGetNamedPipeClientProcessId == nullptr) { + return std::make_tuple( + GetNamedPipeClientProcessId_Result::UnsupportedOs, 0, 0); + } + ULONG pid = 0; + if (!pGetNamedPipeClientProcessId(serverPipe, &pid)) { + return std::make_tuple( + GetNamedPipeClientProcessId_Result::Failure, 0, GetLastError()); + } + return std::make_tuple( + GetNamedPipeClientProcessId_Result::Success, + static_cast<DWORD>(pid), + 0); +} diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h new file mode 100644 index 0000000000..5f9d53aff6 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h @@ -0,0 +1,104 @@ +// Copyright (c) 2016 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 WINPTY_WINDOWS_SECURITY_H +#define WINPTY_WINDOWS_SECURITY_H + +#include <windows.h> +#include <aclapi.h> + +#include <memory> +#include <string> +#include <tuple> +#include <utility> + +// PSID and PSECURITY_DESCRIPTOR are both pointers to void, but we want +// Sid and SecurityDescriptor to be different types. +struct SidTag { typedef PSID type; }; +struct AclTag { typedef PACL type; }; +struct SecurityDescriptorTag { typedef PSECURITY_DESCRIPTOR type; }; + +template <typename T> +class SecurityItem { +public: + struct Impl { + virtual ~Impl() {} + }; + +private: + typedef typename T::type P; + P m_v; + std::unique_ptr<Impl> m_pimpl; + +public: + P get() const { return m_v; } + operator bool() const { return m_v != nullptr; } + + SecurityItem() : m_v(nullptr) {} + SecurityItem(P v, std::unique_ptr<Impl> &&pimpl) : + m_v(v), m_pimpl(std::move(pimpl)) {} + SecurityItem(SecurityItem &&other) : + m_v(other.m_v), m_pimpl(std::move(other.m_pimpl)) { + other.m_v = nullptr; + } + SecurityItem &operator=(SecurityItem &&other) { + m_v = other.m_v; + other.m_v = nullptr; + m_pimpl = std::move(other.m_pimpl); + return *this; + } +}; + +typedef SecurityItem<SidTag> Sid; +typedef SecurityItem<AclTag> Acl; +typedef SecurityItem<SecurityDescriptorTag> SecurityDescriptor; + +Sid getOwnerSid(); +Sid wellKnownSid( + const wchar_t *debuggingName, + SID_IDENTIFIER_AUTHORITY authority, + BYTE authorityCount, + DWORD subAuthority0=0, + DWORD subAuthority1=0); +Sid builtinAdminsSid(); +Sid localSystemSid(); +Sid everyoneSid(); + +SecurityDescriptor createPipeSecurityDescriptorOwnerFullControl(); +SecurityDescriptor createPipeSecurityDescriptorOwnerFullControlEveryoneWrite(); +SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle); + +std::wstring sidToString(PSID sid); +Sid stringToSid(const std::wstring &str); +SecurityDescriptor stringToSd(const std::wstring &str); +std::wstring sdToString(PSECURITY_DESCRIPTOR sd); + +DWORD rejectRemoteClientsPipeFlag(); + +enum class GetNamedPipeClientProcessId_Result { + Success, + Failure, + UnsupportedOs, +}; + +std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD> +getNamedPipeClientProcessId(HANDLE serverPipe); + +#endif // WINPTY_WINDOWS_SECURITY_H diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc new file mode 100644 index 0000000000..d89b00d838 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc @@ -0,0 +1,252 @@ +// Copyright (c) 2016 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 "WindowsVersion.h" + +#include <windows.h> +#include <stdint.h> + +#include <memory> +#include <string> +#include <tuple> + +#include "DebugClient.h" +#include "OsModule.h" +#include "StringBuilder.h" +#include "StringUtil.h" +#include "WinptyAssert.h" +#include "WinptyException.h" + +namespace { + +typedef std::tuple<DWORD, DWORD> Version; + +// This function can only return a version up to 6.2 unless the executable is +// manifested for a newer version of Windows. See the MSDN documentation for +// GetVersionEx. +OSVERSIONINFOEX getWindowsVersionInfo() { + // Allow use of deprecated functions (i.e. GetVersionEx). We need to use + // GetVersionEx for the old MinGW toolchain and with MSVC when it targets XP. + // Having two code paths makes code harder to test, and it's not obvious how + // to detect the presence of a new enough SDK. (Including ntverp.h and + // examining VER_PRODUCTBUILD apparently works, but even then, MinGW-w64 and + // MSVC seem to use different version numbers.) +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) +#endif + OSVERSIONINFOEX info = {}; + info.dwOSVersionInfoSize = sizeof(info); + const auto success = GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info)); + ASSERT(success && "GetVersionEx failed"); + return info; +#ifdef _MSC_VER +#pragma warning(pop) +#endif +} + +Version getWindowsVersion() { + const auto info = getWindowsVersionInfo(); + return Version(info.dwMajorVersion, info.dwMinorVersion); +} + +struct ModuleNotFound : WinptyException { + virtual const wchar_t *what() const WINPTY_NOEXCEPT override { + return L"ModuleNotFound"; + } +}; + +// Throws WinptyException on error. +std::wstring getSystemDirectory() { + wchar_t systemDirectory[MAX_PATH]; + const UINT size = GetSystemDirectoryW(systemDirectory, MAX_PATH); + if (size == 0) { + throwWindowsError(L"GetSystemDirectory failed"); + } else if (size >= MAX_PATH) { + throwWinptyException( + L"GetSystemDirectory: path is longer than MAX_PATH"); + } + return systemDirectory; +} + +#define GET_VERSION_DLL_API(name) \ + const auto p ## name = \ + reinterpret_cast<decltype(name)*>( \ + versionDll.proc(#name)); \ + if (p ## name == nullptr) { \ + throwWinptyException(L ## #name L" is missing"); \ + } + +// Throws WinptyException on error. +VS_FIXEDFILEINFO getFixedFileInfo(const std::wstring &path) { + // version.dll is not a conventional KnownDll, so if we link to it, there's + // a danger of accidentally loading a malicious DLL. In a more typical + // application, perhaps we'd guard against this security issue by + // controlling which directories this code runs in (e.g. *not* the + // "Downloads" directory), but that's harder for the winpty library. + OsModule versionDll( + (getSystemDirectory() + L"\\version.dll").c_str(), + OsModule::LoadErrorBehavior::Throw); + GET_VERSION_DLL_API(GetFileVersionInfoSizeW); + GET_VERSION_DLL_API(GetFileVersionInfoW); + GET_VERSION_DLL_API(VerQueryValueW); + DWORD size = pGetFileVersionInfoSizeW(path.c_str(), nullptr); + if (!size) { + // I see ERROR_FILE_NOT_FOUND on Win7 and + // ERROR_RESOURCE_DATA_NOT_FOUND on WinXP. + if (GetLastError() == ERROR_FILE_NOT_FOUND || + GetLastError() == ERROR_RESOURCE_DATA_NOT_FOUND) { + throw ModuleNotFound(); + } else { + throwWindowsError( + (L"GetFileVersionInfoSizeW failed on " + path).c_str()); + } + } + std::unique_ptr<char[]> versionBuffer(new char[size]); + if (!pGetFileVersionInfoW(path.c_str(), 0, size, versionBuffer.get())) { + throwWindowsError((L"GetFileVersionInfoW failed on " + path).c_str()); + } + VS_FIXEDFILEINFO *versionInfo = nullptr; + UINT versionInfoSize = 0; + if (!pVerQueryValueW( + versionBuffer.get(), L"\\", + reinterpret_cast<void**>(&versionInfo), &versionInfoSize) || + versionInfo == nullptr || + versionInfoSize != sizeof(VS_FIXEDFILEINFO) || + versionInfo->dwSignature != 0xFEEF04BD) { + throwWinptyException((L"VerQueryValueW failed on " + path).c_str()); + } + return *versionInfo; +} + +uint64_t productVersionFromInfo(const VS_FIXEDFILEINFO &info) { + return (static_cast<uint64_t>(info.dwProductVersionMS) << 32) | + (static_cast<uint64_t>(info.dwProductVersionLS)); +} + +uint64_t fileVersionFromInfo(const VS_FIXEDFILEINFO &info) { + return (static_cast<uint64_t>(info.dwFileVersionMS) << 32) | + (static_cast<uint64_t>(info.dwFileVersionLS)); +} + +std::string versionToString(uint64_t version) { + StringBuilder b(32); + b << ((uint16_t)(version >> 48)); + b << '.'; + b << ((uint16_t)(version >> 32)); + b << '.'; + b << ((uint16_t)(version >> 16)); + b << '.'; + b << ((uint16_t)(version >> 0)); + return b.str_moved(); +} + +} // anonymous namespace + +// Returns true for Windows Vista (or Windows Server 2008) or newer. +bool isAtLeastWindowsVista() { + return getWindowsVersion() >= Version(6, 0); +} + +// Returns true for Windows 7 (or Windows Server 2008 R2) or newer. +bool isAtLeastWindows7() { + return getWindowsVersion() >= Version(6, 1); +} + +// Returns true for Windows 8 (or Windows Server 2012) or newer. +bool isAtLeastWindows8() { + return getWindowsVersion() >= Version(6, 2); +} + +#define WINPTY_IA32 1 +#define WINPTY_X64 2 + +#if defined(_M_IX86) || defined(__i386__) +#define WINPTY_ARCH WINPTY_IA32 +#elif defined(_M_X64) || defined(__x86_64__) +#define WINPTY_ARCH WINPTY_X64 +#endif + +typedef BOOL WINAPI IsWow64Process_t(HANDLE hProcess, PBOOL Wow64Process); + +void dumpWindowsVersion() { + if (!isTracingEnabled()) { + return; + } + const auto info = getWindowsVersionInfo(); + StringBuilder b; + b << info.dwMajorVersion << '.' << info.dwMinorVersion + << '.' << info.dwBuildNumber << ' ' + << "SP" << info.wServicePackMajor << '.' << info.wServicePackMinor + << ' '; + switch (info.wProductType) { + case VER_NT_WORKSTATION: b << "Client"; break; + case VER_NT_DOMAIN_CONTROLLER: b << "DomainController"; break; + case VER_NT_SERVER: b << "Server"; break; + default: + b << "product=" << info.wProductType; break; + } + b << ' '; +#if WINPTY_ARCH == WINPTY_IA32 + b << "IA32"; + OsModule kernel32(L"kernel32.dll"); + IsWow64Process_t *pIsWow64Process = + reinterpret_cast<IsWow64Process_t*>( + kernel32.proc("IsWow64Process")); + if (pIsWow64Process != nullptr) { + BOOL result = false; + const BOOL success = pIsWow64Process(GetCurrentProcess(), &result); + if (!success) { + b << " WOW64:error"; + } else if (success && result) { + b << " WOW64"; + } + } else { + b << " WOW64:missingapi"; + } +#elif WINPTY_ARCH == WINPTY_X64 + b << "X64"; +#endif + const auto dllVersion = [](const wchar_t *dllPath) -> std::string { + try { + const auto info = getFixedFileInfo(dllPath); + StringBuilder fb(64); + fb << utf8FromWide(dllPath) << ':'; + fb << "F:" << versionToString(fileVersionFromInfo(info)) << '/' + << "P:" << versionToString(productVersionFromInfo(info)); + return fb.str_moved(); + } catch (const ModuleNotFound&) { + return utf8FromWide(dllPath) + ":none"; + } catch (const WinptyException &e) { + trace("Error getting %s version: %s", + utf8FromWide(dllPath).c_str(), utf8FromWide(e.what()).c_str()); + return utf8FromWide(dllPath) + ":error"; + } + }; + b << ' ' << dllVersion(L"kernel32.dll"); + // ConEmu provides a DLL that hooks many Windows APIs, especially console + // APIs. Its existence and version number could be useful in debugging. +#if WINPTY_ARCH == WINPTY_IA32 + b << ' ' << dllVersion(L"ConEmuHk.dll"); +#elif WINPTY_ARCH == WINPTY_X64 + b << ' ' << dllVersion(L"ConEmuHk64.dll"); +#endif + trace("Windows version: %s", b.c_str()); +} diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h new file mode 100644 index 0000000000..a80798417e --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h @@ -0,0 +1,29 @@ +// Copyright (c) 2016 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 WINPTY_SHARED_WINDOWS_VERSION_H +#define WINPTY_SHARED_WINDOWS_VERSION_H + +bool isAtLeastWindowsVista(); +bool isAtLeastWindows7(); +bool isAtLeastWindows8(); +void dumpWindowsVersion(); + +#endif // WINPTY_SHARED_WINDOWS_VERSION_H diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc new file mode 100644 index 0000000000..1ff0de475a --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2011-2016 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 "WinptyAssert.h" + +#include <windows.h> +#include <stdlib.h> + +#include "DebugClient.h" + +void assertTrace(const char *file, int line, const char *cond) { + trace("Assertion failed: %s, file %s, line %d", + cond, file, line); +} + +#ifdef WINPTY_AGENT_ASSERT + +void agentShutdown() { + HWND hwnd = GetConsoleWindow(); + if (hwnd != NULL) { + PostMessage(hwnd, WM_CLOSE, 0, 0); + Sleep(30000); + trace("Agent shutdown: WM_CLOSE did not end agent process"); + } else { + trace("Agent shutdown: GetConsoleWindow() is NULL"); + } + // abort() prints a message to the console, and if it is frozen, then the + // process would hang, so instead use exit(). (We shouldn't ever get here, + // though, because the WM_CLOSE message should have ended this process.) + exit(1); +} + +void agentAssertFail(const char *file, int line, const char *cond) { + assertTrace(file, line, cond); + agentShutdown(); +} + +#endif diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h new file mode 100644 index 0000000000..b2b8b5e64c --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011-2016 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 WINPTY_ASSERT_H +#define WINPTY_ASSERT_H + +#ifdef WINPTY_AGENT_ASSERT + +void agentShutdown(); +void agentAssertFail(const char *file, int line, const char *cond); + +// Calling the standard assert() function does not work in the agent because +// the error message would be printed to the console, and the only way the +// user can see the console is via a working agent! Moreover, the console may +// be frozen, so attempting to write to it would block forever. This custom +// assert function instead sends the message to the DebugServer, then attempts +// to close the console, then quietly exits. +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + agentAssertFail(__FILE__, __LINE__, #cond); \ + } \ + } while(0) + +#else + +void assertTrace(const char *file, int line, const char *cond); + +// In the other targets, log the assert failure to the debugserver, then fail +// using the ordinary assert mechanism. In case assert is compiled out, fail +// using abort. The amount of code inlined is unfortunate, but asserts aren't +// used much outside the agent. +#include <assert.h> +#include <stdlib.h> +#define ASSERT_CONDITION(cond) (false && (cond)) +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + assertTrace(__FILE__, __LINE__, #cond); \ + assert(ASSERT_CONDITION(#cond)); \ + abort(); \ + } \ + } while(0) + +#endif + +#endif // WINPTY_ASSERT_H diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyException.cc b/src/libs/3rdparty/winpty/src/shared/WinptyException.cc new file mode 100644 index 0000000000..d0d48823d2 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WinptyException.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2016 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 "WinptyException.h" + +#include <memory> +#include <string> + +#include "StringBuilder.h" + +namespace { + +class ExceptionImpl : public WinptyException { +public: + ExceptionImpl(const wchar_t *what) : + m_what(std::make_shared<std::wstring>(what)) {} + virtual const wchar_t *what() const WINPTY_NOEXCEPT override { + return m_what->c_str(); + } +private: + // Using a shared_ptr ensures that copying the object raises no exception. + std::shared_ptr<std::wstring> m_what; +}; + +} // anonymous namespace + +void throwWinptyException(const wchar_t *what) { + throw ExceptionImpl(what); +} + +void throwWindowsError(const wchar_t *prefix, DWORD errorCode) { + WStringBuilder sb(64); + if (prefix != nullptr) { + sb << prefix << L": "; + } + // It might make sense to use FormatMessage here, but IIRC, its API is hard + // to figure out. + sb << L"Windows error " << errorCode; + throwWinptyException(sb.c_str()); +} diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyException.h b/src/libs/3rdparty/winpty/src/shared/WinptyException.h new file mode 100644 index 0000000000..ec353369e5 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WinptyException.h @@ -0,0 +1,43 @@ +// Copyright (c) 2016 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 WINPTY_EXCEPTION_H +#define WINPTY_EXCEPTION_H + +#include <windows.h> + +#if defined(__GNUC__) +#define WINPTY_NOEXCEPT noexcept +#elif defined(_MSC_VER) && _MSC_VER >= 1900 +#define WINPTY_NOEXCEPT noexcept +#else +#define WINPTY_NOEXCEPT +#endif + +class WinptyException { +public: + virtual const wchar_t *what() const WINPTY_NOEXCEPT = 0; + virtual ~WinptyException() {} +}; + +void throwWinptyException(const wchar_t *what); +void throwWindowsError(const wchar_t *prefix, DWORD error=GetLastError()); + +#endif // WINPTY_EXCEPTION_H diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc new file mode 100644 index 0000000000..76bb8a584d --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc @@ -0,0 +1,42 @@ +// 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 "WinptyVersion.h" + +#include <stdio.h> +#include <string.h> + +#include "DebugClient.h" + +// This header is auto-generated by either the Makefile (Unix) or +// UpdateGenVersion.bat (gyp). It is placed in a 'gen' directory, which is +// added to the search path. +#include "GenVersion.h" + +void dumpVersionToStdout() { + printf("winpty version %s\n", GenVersion_Version); + printf("commit %s\n", GenVersion_Commit); +} + +void dumpVersionToTrace() { + trace("winpty version %s (commit %s)", + GenVersion_Version, + GenVersion_Commit); +} diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h new file mode 100644 index 0000000000..e6224d7b84 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h @@ -0,0 +1,27 @@ +// 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 WINPTY_VERSION_H +#define WINPTY_VERSION_H + +void dumpVersionToStdout(); +void dumpVersionToTrace(); + +#endif // WINPTY_VERSION_H diff --git a/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h b/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h new file mode 100644 index 0000000000..e716f245e8 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h @@ -0,0 +1,99 @@ +// Copyright (c) 2016 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 WINPTY_SNPRINTF_H +#define WINPTY_SNPRINTF_H + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "WinptyAssert.h" + +#if defined(__CYGWIN__) || defined(__MSYS__) +#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg) \ + __attribute__((format(printf, (fmtarg), ((vararg))))) +#elif defined(__GNUC__) +#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg) \ + __attribute__((format(ms_printf, (fmtarg), ((vararg))))) +#else +#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg) +#endif + +// Returns a value between 0 and size - 1 (inclusive) on success. Returns -1 +// on failure (including truncation). The output buffer is always +// NUL-terminated. +inline int +winpty_vsnprintf(char *out, size_t size, const char *fmt, va_list ap) { + ASSERT(size > 0); + out[0] = '\0'; +#if defined(_MSC_VER) && _MSC_VER < 1900 + // MSVC 2015 added a C99-conforming vsnprintf. + int count = _vsnprintf_s(out, size, _TRUNCATE, fmt, ap); +#else + // MinGW configurations frequently provide a vsnprintf function that simply + // calls one of the MS _vsnprintf* functions, which are not C99 conformant. + int count = vsnprintf(out, size, fmt, ap); +#endif + if (count < 0 || static_cast<size_t>(count) >= size) { + // On truncation, some *printf* implementations return the + // non-truncated size, but other implementations returns -1. Return + // -1 for consistency. + count = -1; + // Guarantee NUL termination. + out[size - 1] = '\0'; + } else { + // Guarantee NUL termination. + out[count] = '\0'; + } + return count; +} + +// Wraps winpty_vsnprintf. +inline int winpty_snprintf(char *out, size_t size, const char *fmt, ...) + WINPTY_SNPRINTF_FORMAT(3, 4); +inline int winpty_snprintf(char *out, size_t size, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + const int count = winpty_vsnprintf(out, size, fmt, ap); + va_end(ap); + return count; +} + +// Wraps winpty_vsnprintf with automatic size determination. +template <size_t size> +int winpty_vsnprintf(char (&out)[size], const char *fmt, va_list ap) { + return winpty_vsnprintf(out, size, fmt, ap); +} + +// Wraps winpty_vsnprintf with automatic size determination. +template <size_t size> +int winpty_snprintf(char (&out)[size], const char *fmt, ...) + WINPTY_SNPRINTF_FORMAT(2, 3); +template <size_t size> +int winpty_snprintf(char (&out)[size], const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + const int count = winpty_vsnprintf(out, size, fmt, ap); + va_end(ap); + return count; +} + +#endif // WINPTY_SNPRINTF_H diff --git a/src/libs/3rdparty/winpty/src/subdir.mk b/src/libs/3rdparty/winpty/src/subdir.mk new file mode 100644 index 0000000000..9ae8031b08 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/subdir.mk @@ -0,0 +1,5 @@ +include src/agent/subdir.mk +include src/debugserver/subdir.mk +include src/libwinpty/subdir.mk +include src/tests/subdir.mk +include src/unix-adapter/subdir.mk diff --git a/src/libs/3rdparty/winpty/src/tests/subdir.mk b/src/libs/3rdparty/winpty/src/tests/subdir.mk new file mode 100644 index 0000000000..18799c4a5a --- /dev/null +++ b/src/libs/3rdparty/winpty/src/tests/subdir.mk @@ -0,0 +1,28 @@ +# 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. + +build/%.exe : src/tests/%.cc build/winpty.dll + $(info Building $@) + @$(MINGW_CXX) $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS) -o $@ $^ + +TEST_PROGRAMS = \ + build/trivial_test.exe + +-include $(TEST_PROGRAMS:.exe=.d) diff --git a/src/libs/3rdparty/winpty/src/tests/trivial_test.cc b/src/libs/3rdparty/winpty/src/tests/trivial_test.cc new file mode 100644 index 0000000000..2188a4befb --- /dev/null +++ b/src/libs/3rdparty/winpty/src/tests/trivial_test.cc @@ -0,0 +1,158 @@ +// 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 <windows.h> + +#include <cassert> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cwchar> +#include <vector> + +#include "../include/winpty.h" +#include "../shared/DebugClient.h" + +static std::vector<unsigned char> filterContent( + const std::vector<unsigned char> &content) { + std::vector<unsigned char> result; + auto it = content.begin(); + const auto itEnd = content.end(); + while (it < itEnd) { + if (*it == '\r') { + // Filter out carriage returns. Sometimes the output starts with + // a single CR; other times, it has multiple CRs. + it++; + } else if (*it == '\x1b' && (it + 1) < itEnd && *(it + 1) == '[') { + // Filter out escape sequences. They have no interior letters and + // end with a single letter. + it += 2; + while (it < itEnd && !isalpha(*it)) { + it++; + } + it++; + } else { + // Let everything else through. + result.push_back(*it); + it++; + } + } + return result; +} + +// Read bytes from the non-overlapped file handle until the file is closed or +// until an I/O error occurs. +static std::vector<unsigned char> readAll(HANDLE handle) { + unsigned char buf[1024]; + std::vector<unsigned char> result; + while (true) { + DWORD amount = 0; + BOOL ret = ReadFile(handle, buf, sizeof(buf), &amount, nullptr); + if (!ret || amount == 0) { + break; + } + result.insert(result.end(), buf, buf + amount); + } + return result; +} + +static void parentTest() { + wchar_t program[1024]; + wchar_t cmdline[1024]; + GetModuleFileNameW(nullptr, program, 1024); + + { + // XXX: We'd like to use swprintf, which is part of C99 and takes a + // size_t maxlen argument. MinGW-w64 has this function, as does MSVC. + // The old MinGW doesn't, though -- instead, it apparently provides an + // swprintf taking no maxlen argument. This *might* be a regression? + // (There is also no swnprintf, but that function is obsolescent with a + // correct swprintf, and it isn't in POSIX or ISO C.) + // + // Visual C++ 6 also provided this non-conformant swprintf, and I'm + // guessing MSVCRT.DLL does too. (My impression is that the old MinGW + // prefers to rely on MSVCRT.DLL for convenience?) + // + // I could compile differently for old MinGW, but what if it fixes its + // function later? Instead, use a workaround. It's starting to make + // sense to drop MinGW support in favor of MinGW-w64. This is too + // annoying. + // + // grepbait: OLD-MINGW / WINPTY_TARGET_MSYS1 + cmdline[0] = L'\0'; + wcscat(cmdline, L"\""); + wcscat(cmdline, program); + wcscat(cmdline, L"\" CHILD"); + } + // swnprintf(cmdline, sizeof(cmdline) / sizeof(cmdline[0]), + // L"\"%ls\" CHILD", program); + + auto agentCfg = winpty_config_new(0, nullptr); + assert(agentCfg != nullptr); + auto pty = winpty_open(agentCfg, nullptr); + assert(pty != nullptr); + winpty_config_free(agentCfg); + + HANDLE conin = CreateFileW( + winpty_conin_name(pty), + GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); + HANDLE conout = CreateFileW( + winpty_conout_name(pty), + GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr); + assert(conin != INVALID_HANDLE_VALUE); + assert(conout != INVALID_HANDLE_VALUE); + + auto spawnCfg = winpty_spawn_config_new( + WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, program, cmdline, + nullptr, nullptr, nullptr); + assert(spawnCfg != nullptr); + HANDLE process = nullptr; + BOOL spawnSuccess = winpty_spawn( + pty, spawnCfg, &process, nullptr, nullptr, nullptr); + assert(spawnSuccess && process != nullptr); + + auto content = readAll(conout); + content = filterContent(content); + + std::vector<unsigned char> expectedContent = { + 'H', 'I', '\n', 'X', 'Y', '\n' + }; + DWORD exitCode = 0; + assert(GetExitCodeProcess(process, &exitCode) && exitCode == 42); + CloseHandle(process); + CloseHandle(conin); + CloseHandle(conout); + assert(content == expectedContent); + winpty_free(pty); +} + +static void childTest() { + printf("HI\nXY\n"); + exit(42); +} + +int main(int argc, char *argv[]) { + if (argc == 1) { + parentTest(); + } else { + childTest(); + } + return 0; +} diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc new file mode 100644 index 0000000000..39f1e09685 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2011-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 "InputHandler.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <sys/select.h> +#include <unistd.h> + +#include <algorithm> +#include <vector> + +#include "../shared/DebugClient.h" +#include "Util.h" +#include "WakeupFd.h" + +InputHandler::InputHandler( + HANDLE conin, int inputfd, WakeupFd &completionWakeup) : + m_conin(conin), + m_inputfd(inputfd), + m_completionWakeup(completionWakeup), + m_threadHasBeenJoined(false), + m_shouldShutdown(0), + m_threadCompleted(0) +{ + pthread_create(&m_thread, NULL, InputHandler::threadProcS, this); +} + +void InputHandler::shutdown() { + startShutdown(); + if (!m_threadHasBeenJoined) { + int ret = pthread_join(m_thread, NULL); + assert(ret == 0 && "pthread_join failed"); + m_threadHasBeenJoined = true; + } +} + +void InputHandler::threadProc() { + std::vector<char> buffer(4096); + fd_set readfds; + FD_ZERO(&readfds); + while (true) { + // Handle shutdown. + m_wakeup.reset(); + if (m_shouldShutdown) { + trace("InputHandler: shutting down"); + break; + } + + // Block until data arrives. + { + const int max_fd = std::max(m_inputfd, m_wakeup.fd()); + FD_SET(m_inputfd, &readfds); + FD_SET(m_wakeup.fd(), &readfds); + selectWrapper("InputHandler", max_fd + 1, &readfds); + if (!FD_ISSET(m_inputfd, &readfds)) { + continue; + } + } + + const int numRead = read(m_inputfd, &buffer[0], buffer.size()); + if (numRead == -1 && errno == EINTR) { + // Apparently, this read is interrupted on Cygwin 1.7 by a SIGWINCH + // signal even though I set the SA_RESTART flag on the handler. + continue; + } + + // tty is closed, or the read failed for some unexpected reason. + if (numRead <= 0) { + trace("InputHandler: tty read failed: numRead=%d", numRead); + break; + } + + DWORD written = 0; + BOOL ret = WriteFile(m_conin, + &buffer[0], numRead, + &written, NULL); + if (!ret || written != static_cast<DWORD>(numRead)) { + if (!ret && GetLastError() == ERROR_BROKEN_PIPE) { + trace("InputHandler: pipe closed: written=%u", + static_cast<unsigned int>(written)); + } else { + trace("InputHandler: write failed: " + "ret=%d lastError=0x%x numRead=%d written=%u", + ret, + static_cast<unsigned int>(GetLastError()), + numRead, + static_cast<unsigned int>(written)); + } + break; + } + } + m_threadCompleted = 1; + m_completionWakeup.set(); +} diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h new file mode 100644 index 0000000000..9c3f540d63 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011-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 UNIX_ADAPTER_INPUT_HANDLER_H +#define UNIX_ADAPTER_INPUT_HANDLER_H + +#include <windows.h> +#include <pthread.h> +#include <signal.h> + +#include "WakeupFd.h" + +// Connect a Cygwin blocking fd to winpty CONIN. +class InputHandler { +public: + InputHandler(HANDLE conin, int inputfd, WakeupFd &completionWakeup); + ~InputHandler() { shutdown(); } + bool isComplete() { return m_threadCompleted; } + void startShutdown() { m_shouldShutdown = 1; m_wakeup.set(); } + void shutdown(); + +private: + static void *threadProcS(void *pvthis) { + reinterpret_cast<InputHandler*>(pvthis)->threadProc(); + return NULL; + } + void threadProc(); + + HANDLE m_conin; + int m_inputfd; + pthread_t m_thread; + WakeupFd &m_completionWakeup; + WakeupFd m_wakeup; + bool m_threadHasBeenJoined; + volatile sig_atomic_t m_shouldShutdown; + volatile sig_atomic_t m_threadCompleted; +}; + +#endif // UNIX_ADAPTER_INPUT_HANDLER_H diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc new file mode 100644 index 0000000000..573b8adced --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2011-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 "OutputHandler.h" + +#include <assert.h> +#include <errno.h> +#include <sys/select.h> +#include <unistd.h> + +#include <algorithm> +#include <vector> + +#include "../shared/DebugClient.h" +#include "Util.h" +#include "WakeupFd.h" + +OutputHandler::OutputHandler( + HANDLE conout, int outputfd, WakeupFd &completionWakeup) : + m_conout(conout), + m_outputfd(outputfd), + m_completionWakeup(completionWakeup), + m_threadHasBeenJoined(false), + m_threadCompleted(0) +{ + pthread_create(&m_thread, NULL, OutputHandler::threadProcS, this); +} + +void OutputHandler::shutdown() { + if (!m_threadHasBeenJoined) { + int ret = pthread_join(m_thread, NULL); + assert(ret == 0 && "pthread_join failed"); + m_threadHasBeenJoined = true; + } +} + +void OutputHandler::threadProc() { + std::vector<char> buffer(4096); + while (true) { + DWORD numRead = 0; + BOOL ret = ReadFile(m_conout, + &buffer[0], buffer.size(), + &numRead, NULL); + if (!ret || numRead == 0) { + if (!ret && GetLastError() == ERROR_BROKEN_PIPE) { + trace("OutputHandler: pipe closed: numRead=%u", + static_cast<unsigned int>(numRead)); + } else { + trace("OutputHandler: read failed: " + "ret=%d lastError=0x%x numRead=%u", + ret, + static_cast<unsigned int>(GetLastError()), + static_cast<unsigned int>(numRead)); + } + break; + } + if (!writeAll(m_outputfd, &buffer[0], numRead)) { + break; + } + } + m_threadCompleted = 1; + m_completionWakeup.set(); +} diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h new file mode 100644 index 0000000000..48241c5538 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h @@ -0,0 +1,53 @@ +// Copyright (c) 2011-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 UNIX_ADAPTER_OUTPUT_HANDLER_H +#define UNIX_ADAPTER_OUTPUT_HANDLER_H + +#include <windows.h> +#include <pthread.h> +#include <signal.h> + +#include "WakeupFd.h" + +// Connect winpty CONOUT/CONERR to a Cygwin blocking fd. +class OutputHandler { +public: + OutputHandler(HANDLE conout, int outputfd, WakeupFd &completionWakeup); + ~OutputHandler() { shutdown(); } + bool isComplete() { return m_threadCompleted; } + void shutdown(); + +private: + static void *threadProcS(void *pvthis) { + reinterpret_cast<OutputHandler*>(pvthis)->threadProc(); + return NULL; + } + void threadProc(); + + HANDLE m_conout; + int m_outputfd; + pthread_t m_thread; + WakeupFd &m_completionWakeup; + bool m_threadHasBeenJoined; + volatile sig_atomic_t m_threadCompleted; +}; + +#endif // UNIX_ADAPTER_OUTPUT_HANDLER_H diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc b/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc new file mode 100644 index 0000000000..e13f84a529 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc @@ -0,0 +1,86 @@ +// 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 "Util.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "../shared/DebugClient.h" + +// Write the entire buffer, restarting it as necessary. +bool writeAll(int fd, const void *buffer, size_t size) { + size_t written = 0; + while (written < size) { + int ret = write(fd, + reinterpret_cast<const char*>(buffer) + written, + size - written); + if (ret == -1 && errno == EINTR) { + continue; + } + if (ret <= 0) { + trace("write failed: " + "fd=%d errno=%d size=%u written=%d ret=%d", + fd, + errno, + static_cast<unsigned int>(size), + static_cast<unsigned int>(written), + ret); + return false; + } + assert(static_cast<size_t>(ret) <= size - written); + written += ret; + } + assert(written == size); + return true; +} + +bool writeStr(int fd, const char *str) { + return writeAll(fd, str, strlen(str)); +} + +void selectWrapper(const char *diagName, int nfds, fd_set *readfds) { + int ret = select(nfds, readfds, NULL, NULL, NULL); + if (ret < 0) { + if (errno == EINTR) { + FD_ZERO(readfds); + return; + } +#ifdef WINPTY_TARGET_MSYS1 + // The select system call sometimes fails with EAGAIN instead of EINTR. + // This apparantly only happens with the old Cygwin fork "MSYS" used in + // the mingw.org project. select is not supposed to fail with EAGAIN, + // and EAGAIN does not make much sense as an error code. (The whole + // point of select is to block.) + if (errno == EAGAIN) { + trace("%s select returned EAGAIN: interpreting like EINTR", + diagName); + FD_ZERO(readfds); + return; + } +#endif + fprintf(stderr, "Internal error: %s select failed: " + "error %d", diagName, errno); + abort(); + } +} diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/Util.h b/src/libs/3rdparty/winpty/src/unix-adapter/Util.h new file mode 100644 index 0000000000..cadb4c82a9 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/Util.h @@ -0,0 +1,31 @@ +// 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 UNIX_ADAPTER_UTIL_H +#define UNIX_ADAPTER_UTIL_H + +#include <stdlib.h> +#include <sys/select.h> + +bool writeAll(int fd, const void *buffer, size_t size); +bool writeStr(int fd, const char *str); +void selectWrapper(const char *diagName, int nfds, fd_set *readfds); + +#endif // UNIX_ADAPTER_UTIL_H diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc new file mode 100644 index 0000000000..6b47379015 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2011-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 "WakeupFd.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static void setFdNonBlock(int fd) { + int status = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, status | O_NONBLOCK); +} + +WakeupFd::WakeupFd() { + int pipeFd[2]; + if (pipe(pipeFd) != 0) { + perror("Could not create internal wakeup pipe"); + abort(); + } + m_pipeReadFd = pipeFd[0]; + m_pipeWriteFd = pipeFd[1]; + setFdNonBlock(m_pipeReadFd); + setFdNonBlock(m_pipeWriteFd); +} + +WakeupFd::~WakeupFd() { + close(m_pipeReadFd); + close(m_pipeWriteFd); +} + +void WakeupFd::set() { + char dummy = 0; + int ret; + do { + ret = write(m_pipeWriteFd, &dummy, 1); + } while (ret < 0 && errno == EINTR); +} + +void WakeupFd::reset() { + char tmpBuf[256]; + while (true) { + int amount = read(m_pipeReadFd, tmpBuf, sizeof(tmpBuf)); + if (amount < 0 && errno == EAGAIN) { + break; + } else if (amount <= 0) { + perror("error reading from internal wakeup pipe"); + abort(); + } + } +} diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h new file mode 100644 index 0000000000..dd8d362aa1 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h @@ -0,0 +1,42 @@ +// 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 UNIX_ADAPTER_WAKEUP_FD_H +#define UNIX_ADAPTER_WAKEUP_FD_H + +class WakeupFd { +public: + WakeupFd(); + ~WakeupFd(); + int fd() { return m_pipeReadFd; } + void set(); + void reset(); + +private: + // Do not allow copying the WakeupFd object. + WakeupFd(const WakeupFd &other); + WakeupFd &operator=(const WakeupFd &other); + +private: + int m_pipeReadFd; + int m_pipeWriteFd; +}; + +#endif // UNIX_ADAPTER_WAKEUP_FD_H diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/main.cc b/src/libs/3rdparty/winpty/src/unix-adapter/main.cc new file mode 100644 index 0000000000..992cb70e44 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/main.cc @@ -0,0 +1,729 @@ +// Copyright (c) 2011-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. + +// MSYS's sys/cygwin.h header only declares cygwin_internal if WINVER is +// defined, which is defined in windows.h. Therefore, include windows.h early. +#include <windows.h> + +#include <assert.h> +#include <cygwin/version.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/cygwin.h> +#include <termios.h> +#include <unistd.h> + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include <winpty.h> +#include "../shared/DebugClient.h" +#include "../shared/UnixCtrlChars.h" +#include "../shared/WinptyVersion.h" +#include "InputHandler.h" +#include "OutputHandler.h" +#include "Util.h" +#include "WakeupFd.h" + +#define CSI "\x1b[" + +static WakeupFd *g_mainWakeup = NULL; + +static WakeupFd &mainWakeup() +{ + if (g_mainWakeup == NULL) { + static const char msg[] = "Internal error: g_mainWakeup is NULL\r\n"; + write(STDERR_FILENO, msg, sizeof(msg) - 1); + abort(); + } + return *g_mainWakeup; +} + +struct SavedTermiosMode { + int count; + bool valid[3]; + termios mode[3]; +}; + +// Put the input terminal into non-canonical mode. +static SavedTermiosMode setRawTerminalMode( + bool allowNonTtys, bool setStdout, bool setStderr) +{ + SavedTermiosMode ret; + const char *const kNames[3] = { "stdin", "stdout", "stderr" }; + + ret.valid[0] = true; + ret.valid[1] = setStdout; + ret.valid[2] = setStderr; + + for (int i = 0; i < 3; ++i) { + if (!ret.valid[i]) { + continue; + } + if (!isatty(i)) { + ret.valid[i] = false; + if (!allowNonTtys) { + fprintf(stderr, "%s is not a tty\n", kNames[i]); + exit(1); + } + } else { + ret.valid[i] = true; + if (tcgetattr(i, &ret.mode[i]) < 0) { + perror("tcgetattr failed"); + exit(1); + } + } + } + + if (ret.valid[STDIN_FILENO]) { + termios buf; + if (tcgetattr(STDIN_FILENO, &buf) < 0) { + perror("tcgetattr failed"); + exit(1); + } + buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + buf.c_cflag &= ~(CSIZE | PARENB); + buf.c_cflag |= CS8; + buf.c_cc[VMIN] = 1; // blocking read + buf.c_cc[VTIME] = 0; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf) < 0) { + fprintf(stderr, "tcsetattr failed\n"); + exit(1); + } + } + + for (int i = STDOUT_FILENO; i <= STDERR_FILENO; ++i) { + if (!ret.valid[i]) { + continue; + } + termios buf; + if (tcgetattr(i, &buf) < 0) { + perror("tcgetattr failed"); + exit(1); + } + buf.c_cflag &= ~(CSIZE | PARENB); + buf.c_cflag |= CS8; + buf.c_oflag &= ~OPOST; + if (tcsetattr(i, TCSAFLUSH, &buf) < 0) { + fprintf(stderr, "tcsetattr failed\n"); + exit(1); + } + } + + return ret; +} + +static void restoreTerminalMode(const SavedTermiosMode &original) +{ + for (int i = 0; i < 3; ++i) { + if (!original.valid[i]) { + continue; + } + if (tcsetattr(i, TCSAFLUSH, &original.mode[i]) < 0) { + perror("error restoring terminal mode"); + exit(1); + } + } +} + +static void debugShowKey(bool allowNonTtys) +{ + printf("\nPress any keys -- Ctrl-D exits\n\n"); + const SavedTermiosMode saved = + setRawTerminalMode(allowNonTtys, false, false); + char buf[128]; + while (true) { + const ssize_t len = read(STDIN_FILENO, buf, sizeof(buf)); + if (len <= 0) { + break; + } + for (int i = 0; i < len; ++i) { + char ctrl = decodeUnixCtrlChar(buf[i]); + if (ctrl == '\0') { + putchar(buf[i]); + } else { + putchar('^'); + putchar(ctrl); + } + } + for (int i = 0; i < len; ++i) { + unsigned char uch = buf[i]; + printf("\t%3d %04o 0x%02x\n", uch, uch, uch); + fflush(stdout); + } + if (buf[0] == 4) { + // Ctrl-D + break; + } + } + restoreTerminalMode(saved); +} + +static void terminalResized(int signo) +{ + mainWakeup().set(); +} + +static void registerResizeSignalHandler() +{ + struct sigaction resizeSigAct; + memset(&resizeSigAct, 0, sizeof(resizeSigAct)); + resizeSigAct.sa_handler = terminalResized; + resizeSigAct.sa_flags = SA_RESTART; + sigaction(SIGWINCH, &resizeSigAct, NULL); +} + +// Convert the path to a Win32 path if it is a POSIX path, and convert slashes +// to backslashes. +static std::string convertPosixPathToWin(const std::string &path) +{ + char *tmp; +#if defined(CYGWIN_VERSION_CYGWIN_CONV) && \ + CYGWIN_VERSION_API_MINOR >= CYGWIN_VERSION_CYGWIN_CONV + // MSYS2 and versions of Cygwin released after 2009 or so use this API. + // The original MSYS still lacks this API. + ssize_t newSize = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, + path.c_str(), NULL, 0); + assert(newSize >= 0); + tmp = new char[newSize + 1]; + ssize_t success = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, + path.c_str(), tmp, newSize + 1); + assert(success == 0); +#else + // In the current Cygwin header file, this API is documented as deprecated + // because it's restricted to paths of MAX_PATH length. In the CVS version + // of MSYS, the newer API doesn't exist, and this older API is implemented + // using msys_p2w, which seems like it would handle paths larger than + // MAX_PATH, but there's no way to query how large the new path is. + // Hopefully, this is large enough. + tmp = new char[MAX_PATH + path.size()]; + cygwin_conv_to_win32_path(path.c_str(), tmp); +#endif + for (int i = 0; tmp[i] != '\0'; ++i) { + if (tmp[i] == '/') + tmp[i] = '\\'; + } + std::string ret(tmp); + delete [] tmp; + return ret; +} + +static std::string resolvePath(const std::string &path) +{ + char ret[PATH_MAX]; + ret[0] = '\0'; + if (realpath(path.c_str(), ret) != ret) { + return std::string(); + } + return ret; +} + +template <size_t N> +static bool endsWith(const std::string &path, const char (&suf)[N]) +{ + const size_t suffixLen = N - 1; + char actualSuf[N]; + if (path.size() < suffixLen) { + return false; + } + strcpy(actualSuf, &path.c_str()[path.size() - suffixLen]); + for (size_t i = 0; i < suffixLen; ++i) { + actualSuf[i] = tolower(actualSuf[i]); + } + return !strcmp(actualSuf, suf); +} + +static std::string findProgram( + const char *winptyProgName, + const std::string &prog) +{ + std::string candidate; + if (prog.find('/') == std::string::npos && + prog.find('\\') == std::string::npos) { + // XXX: It would be nice to use a lambda here (once/if old MSYS support + // is dropped). + // Search the PATH. + const char *const pathVar = getenv("PATH"); + const std::string pathList(pathVar ? pathVar : ""); + size_t elpos = 0; + while (true) { + const size_t elend = pathList.find(':', elpos); + candidate = pathList.substr(elpos, elend - elpos); + if (!candidate.empty() && *(candidate.end() - 1) != '/') { + candidate += '/'; + } + candidate += prog; + candidate = resolvePath(candidate); + if (!candidate.empty()) { + int perm = X_OK; + if (endsWith(candidate, ".bat") || endsWith(candidate, ".cmd")) { +#ifdef __MSYS__ + // In MSYS/MSYS2, batch files don't have the execute bit + // set, so just check that they're readable. + perm = R_OK; +#endif + } else if (endsWith(candidate, ".com") || endsWith(candidate, ".exe")) { + // Do nothing. + } else { + // Make the exe extension explicit so that we don't try to + // run shell scripts with CreateProcess/winpty_spawn. + candidate += ".exe"; + } + if (!access(candidate.c_str(), perm)) { + break; + } + } + if (elend == std::string::npos) { + fprintf(stderr, "%s: error: cannot start '%s': Not found in PATH\n", + winptyProgName, prog.c_str()); + exit(1); + } else { + elpos = elend + 1; + } + } + } else { + candidate = resolvePath(prog); + if (candidate.empty()) { + std::string errstr(strerror(errno)); + fprintf(stderr, "%s: error: cannot start '%s': %s\n", + winptyProgName, prog.c_str(), errstr.c_str()); + exit(1); + } + } + return convertPosixPathToWin(candidate); +} + +// Convert argc/argv into a Win32 command-line following the escaping convention +// documented on MSDN. (e.g. see CommandLineToArgvW documentation) +static std::string argvToCommandLine(const std::vector<std::string> &argv) +{ + std::string result; + for (size_t argIndex = 0; argIndex < argv.size(); ++argIndex) { + if (argIndex > 0) + result.push_back(' '); + const char *arg = argv[argIndex].c_str(); + const bool quote = + strchr(arg, ' ') != NULL || + strchr(arg, '\t') != NULL || + *arg == '\0'; + if (quote) + result.push_back('\"'); + int bsCount = 0; + for (const char *p = arg; *p != '\0'; ++p) { + if (*p == '\\') { + bsCount++; + } else if (*p == '\"') { + result.append(bsCount * 2 + 1, '\\'); + result.push_back('\"'); + bsCount = 0; + } else { + result.append(bsCount, '\\'); + bsCount = 0; + result.push_back(*p); + } + } + if (quote) { + result.append(bsCount * 2, '\\'); + result.push_back('\"'); + } else { + result.append(bsCount, '\\'); + } + } + return result; +} + +static wchar_t *heapMbsToWcs(const char *text) +{ + // Calling mbstowcs with a NULL first argument seems to be broken on MSYS. + // Instead of returning the size of the converted string, it returns 0. + // Using strlen(text) * 2 is probably big enough. + size_t maxLen = strlen(text) * 2 + 1; + wchar_t *ret = new wchar_t[maxLen]; + size_t len = mbstowcs(ret, text, maxLen); + assert(len != (size_t)-1 && len < maxLen); + return ret; +} + +static char *heapWcsToMbs(const wchar_t *text) +{ + // Calling wcstombs with a NULL first argument seems to be broken on MSYS. + // Instead of returning the size of the converted string, it returns 0. + // Using wcslen(text) * 3 is big enough for UTF-8 and probably other + // encodings. For UTF-8, codepoints that fit in a single wchar + // (U+0000 to U+FFFF) are encoded using 1-3 bytes. The remaining code + // points needs two wchar's and are encoded using 4 bytes. + size_t maxLen = wcslen(text) * 3 + 1; + char *ret = new char[maxLen]; + size_t len = wcstombs(ret, text, maxLen); + if (len == (size_t)-1 || len >= maxLen) { + delete [] ret; + return NULL; + } else { + return ret; + } +} + +static std::string wcsToMbs(const wchar_t *text) +{ + std::string ret; + const char *ptr = heapWcsToMbs(text); + if (ptr != NULL) { + ret = ptr; + delete [] ptr; + } + return ret; +} + +void setupWin32Environment() +{ + std::map<std::string, std::string> varsToCopy; + const char *vars[] = { + "WINPTY_DEBUG", + "WINPTY_SHOW_CONSOLE", + NULL + }; + for (int i = 0; vars[i] != NULL; ++i) { + const char *cstr = getenv(vars[i]); + if (cstr != NULL && cstr[0] != '\0') { + varsToCopy[vars[i]] = cstr; + } + } + +#if defined(__MSYS__) && CYGWIN_VERSION_API_MINOR >= 48 || \ + !defined(__MSYS__) && CYGWIN_VERSION_API_MINOR >= 153 + // Use CW_SYNC_WINENV to copy the Unix environment to the Win32 + // environment. The command performs special translation on some variables + // (such as PATH and TMP). It also copies the debugging environment + // variables. + // + // Note that the API minor versions have diverged in Cygwin and MSYS. + // CW_SYNC_WINENV was added to Cygwin in version 153. (Cygwin's + // include/cygwin/version.h says that CW_SETUP_WINENV was added in 153. + // The flag was renamed 8 days after it was added, but the API docs weren't + // updated.) The flag was added to MSYS in version 48. + // + // Also, in my limited testing, this call seems to be necessary with Cygwin + // but unnecessary with MSYS. Perhaps MSYS is automatically syncing the + // Unix environment with the Win32 environment before starting console.exe? + // It shouldn't hurt to call it for MSYS. + cygwin_internal(CW_SYNC_WINENV); +#endif + + // Copy debugging environment variables from the Cygwin environment + // to the Win32 environment so the agent will inherit it. + for (std::map<std::string, std::string>::iterator it = varsToCopy.begin(); + it != varsToCopy.end(); + ++it) { + wchar_t *nameW = heapMbsToWcs(it->first.c_str()); + wchar_t *valueW = heapMbsToWcs(it->second.c_str()); + SetEnvironmentVariableW(nameW, valueW); + delete [] nameW; + delete [] valueW; + } + + // Clear the TERM variable. The child process's immediate console/terminal + // environment is a Windows console, not the terminal that winpty is + // communicating with. Leaving the TERM variable set can break programs in + // various ways. (e.g. arrows keys broken in Cygwin less, IronPython's + // help(...) function doesn't start, misc programs decide they should + // output color escape codes on pre-Win10). See + // https://github.com/rprichard/winpty/issues/43. + SetEnvironmentVariableW(L"TERM", NULL); +} + +static void usage(const char *program, int exitCode) +{ + printf("Usage: %s [options] [--] program [args]\n", program); + printf("\n"); + printf("Options:\n"); + printf(" -h, --help Show this help message\n"); + printf(" --mouse Enable terminal mouse input\n"); + printf(" --showkey Dump STDIN escape sequences\n"); + printf(" --version Show the winpty version number\n"); + exit(exitCode); +} + +struct Arguments { + std::vector<std::string> childArgv; + bool mouseInput; + bool testAllowNonTtys; + bool testConerr; + bool testPlainOutput; + bool testColorEscapes; +}; + +static void parseArguments(int argc, char *argv[], Arguments &out) +{ + out.mouseInput = false; + out.testAllowNonTtys = false; + out.testConerr = false; + out.testPlainOutput = false; + out.testColorEscapes = false; + bool doShowKeys = false; + const char *const program = argc >= 1 ? argv[0] : "<program>"; + int argi = 1; + while (argi < argc) { + std::string arg(argv[argi++]); + if (arg.size() >= 1 && arg[0] == '-') { + if (arg == "-h" || arg == "--help") { + usage(program, 0); + } else if (arg == "--mouse") { + out.mouseInput = true; + } else if (arg == "--showkey") { + doShowKeys = true; + } else if (arg == "--version") { + dumpVersionToStdout(); + exit(0); + } else if (arg == "-Xallow-non-tty") { + out.testAllowNonTtys = true; + } else if (arg == "-Xconerr") { + out.testConerr = true; + } else if (arg == "-Xplain") { + out.testPlainOutput = true; + } else if (arg == "-Xcolor") { + out.testColorEscapes = true; + } else if (arg == "--") { + break; + } else { + fprintf(stderr, "Error: unrecognized option: '%s'\n", + arg.c_str()); + exit(1); + } + } else { + out.childArgv.push_back(arg); + break; + } + } + for (; argi < argc; ++argi) { + out.childArgv.push_back(argv[argi]); + } + if (doShowKeys) { + debugShowKey(out.testAllowNonTtys); + exit(0); + } + if (out.childArgv.size() == 0) { + usage(program, 1); + } +} + +static std::string errorMessageToString(DWORD err) +{ + // Use FormatMessageW rather than FormatMessageA, because we want to use + // wcstombs to convert to the Cygwin locale, which might not match the + // codepage FormatMessageA would use. We need to convert using wcstombs, + // rather than print using %ls, because %ls doesn't work in the original + // MSYS. + wchar_t *wideMsgPtr = NULL; + const DWORD formatRet = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast<wchar_t*>(&wideMsgPtr), + 0, + NULL); + if (formatRet == 0 || wideMsgPtr == NULL) { + return std::string(); + } + std::string msg = wcsToMbs(wideMsgPtr); + LocalFree(wideMsgPtr); + const size_t pos = msg.find_last_not_of(" \r\n\t"); + if (pos == std::string::npos) { + msg.clear(); + } else { + msg.erase(pos + 1); + } + return msg; +} + +static std::string formatErrorMessage(DWORD err) +{ + char buf[64]; + sprintf(buf, "error %#x", static_cast<unsigned int>(err)); + std::string ret = errorMessageToString(err); + if (ret.empty()) { + ret += buf; + } else { + ret += " ("; + ret += buf; + ret += ")"; + } + return ret; +} + +int main(int argc, char *argv[]) +{ + setlocale(LC_ALL, ""); + + g_mainWakeup = new WakeupFd(); + + Arguments args; + parseArguments(argc, argv, args); + + setupWin32Environment(); + + winsize sz = { 0 }; + sz.ws_col = 80; + sz.ws_row = 25; + ioctl(STDIN_FILENO, TIOCGWINSZ, &sz); + + DWORD agentFlags = WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION; + if (args.testConerr) { agentFlags |= WINPTY_FLAG_CONERR; } + if (args.testPlainOutput) { agentFlags |= WINPTY_FLAG_PLAIN_OUTPUT; } + if (args.testColorEscapes) { agentFlags |= WINPTY_FLAG_COLOR_ESCAPES; } + winpty_config_t *agentCfg = winpty_config_new(agentFlags, NULL); + assert(agentCfg != NULL); + winpty_config_set_initial_size(agentCfg, sz.ws_col, sz.ws_row); + if (args.mouseInput) { + winpty_config_set_mouse_mode(agentCfg, WINPTY_MOUSE_MODE_FORCE); + } + + winpty_error_ptr_t openErr = NULL; + winpty_t *wp = winpty_open(agentCfg, &openErr); + if (wp == NULL) { + fprintf(stderr, "Error creating winpty: %s\n", + wcsToMbs(winpty_error_msg(openErr)).c_str()); + exit(1); + } + winpty_config_free(agentCfg); + winpty_error_free(openErr); + + HANDLE conin = CreateFileW(winpty_conin_name(wp), GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + HANDLE conout = CreateFileW(winpty_conout_name(wp), GENERIC_READ, 0, NULL, + OPEN_EXISTING, 0, NULL); + assert(conin != INVALID_HANDLE_VALUE); + assert(conout != INVALID_HANDLE_VALUE); + HANDLE conerr = NULL; + if (args.testConerr) { + conerr = CreateFileW(winpty_conerr_name(wp), GENERIC_READ, 0, NULL, + OPEN_EXISTING, 0, NULL); + assert(conerr != INVALID_HANDLE_VALUE); + } + + HANDLE childHandle = NULL; + + { + // Start the child process under the console. + args.childArgv[0] = findProgram(argv[0], args.childArgv[0]); + std::string cmdLine = argvToCommandLine(args.childArgv); + wchar_t *cmdLineW = heapMbsToWcs(cmdLine.c_str()); + + winpty_spawn_config_t *spawnCfg = winpty_spawn_config_new( + WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, + NULL, cmdLineW, NULL, NULL, NULL); + assert(spawnCfg != NULL); + + winpty_error_ptr_t spawnErr = NULL; + DWORD lastError = 0; + BOOL spawnRet = winpty_spawn(wp, spawnCfg, &childHandle, NULL, + &lastError, &spawnErr); + winpty_spawn_config_free(spawnCfg); + + if (!spawnRet) { + winpty_result_t spawnCode = winpty_error_code(spawnErr); + if (spawnCode == WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED) { + fprintf(stderr, "%s: error: cannot start '%s': %s\n", + argv[0], + cmdLine.c_str(), + formatErrorMessage(lastError).c_str()); + } else { + fprintf(stderr, "%s: error: cannot start '%s': internal error: %s\n", + argv[0], + cmdLine.c_str(), + wcsToMbs(winpty_error_msg(spawnErr)).c_str()); + } + exit(1); + } + winpty_error_free(spawnErr); + delete [] cmdLineW; + } + + registerResizeSignalHandler(); + SavedTermiosMode mode = + setRawTerminalMode(args.testAllowNonTtys, true, args.testConerr); + + InputHandler inputHandler(conin, STDIN_FILENO, mainWakeup()); + OutputHandler outputHandler(conout, STDOUT_FILENO, mainWakeup()); + OutputHandler *errorHandler = NULL; + if (args.testConerr) { + errorHandler = new OutputHandler(conerr, STDERR_FILENO, mainWakeup()); + } + + while (true) { + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(mainWakeup().fd(), &readfds); + selectWrapper("main thread", mainWakeup().fd() + 1, &readfds); + mainWakeup().reset(); + + // Check for terminal resize. + { + winsize sz2; + ioctl(STDIN_FILENO, TIOCGWINSZ, &sz2); + if (memcmp(&sz, &sz2, sizeof(sz)) != 0) { + sz = sz2; + winpty_set_size(wp, sz.ws_col, sz.ws_row, NULL); + } + } + + // Check for an I/O handler shutting down (possibly indicating that the + // child process has exited). + if (inputHandler.isComplete() || outputHandler.isComplete() || + (errorHandler != NULL && errorHandler->isComplete())) { + break; + } + } + + // Kill the agent connection. This will kill the agent, closing the CONIN + // and CONOUT pipes on the agent pipe, prompting our I/O handler to shut + // down. + winpty_free(wp); + + inputHandler.shutdown(); + outputHandler.shutdown(); + CloseHandle(conin); + CloseHandle(conout); + + if (errorHandler != NULL) { + errorHandler->shutdown(); + delete errorHandler; + CloseHandle(conerr); + } + + restoreTerminalMode(mode); + + DWORD exitCode = 0; + if (!GetExitCodeProcess(childHandle, &exitCode)) { + exitCode = 1; + } + CloseHandle(childHandle); + return exitCode; +} diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk b/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk new file mode 100644 index 0000000000..200193a1b1 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk @@ -0,0 +1,41 @@ +# Copyright (c) 2011-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. + +ALL_TARGETS += build/$(UNIX_ADAPTER_EXE) + +$(eval $(call def_unix_target,unix-adapter,)) + +UNIX_ADAPTER_OBJECTS = \ + build/unix-adapter/unix-adapter/InputHandler.o \ + build/unix-adapter/unix-adapter/OutputHandler.o \ + build/unix-adapter/unix-adapter/Util.o \ + build/unix-adapter/unix-adapter/WakeupFd.o \ + build/unix-adapter/unix-adapter/main.o \ + build/unix-adapter/shared/DebugClient.o \ + build/unix-adapter/shared/WinptyAssert.o \ + build/unix-adapter/shared/WinptyVersion.o + +build/unix-adapter/shared/WinptyVersion.o : build/gen/GenVersion.h + +build/$(UNIX_ADAPTER_EXE) : $(UNIX_ADAPTER_OBJECTS) build/winpty.dll + $(info Linking $@) + @$(UNIX_CXX) $(UNIX_LDFLAGS) -o $@ $^ + +-include $(UNIX_ADAPTER_OBJECTS:.o=.d) diff --git a/src/libs/3rdparty/winpty/src/winpty.gyp b/src/libs/3rdparty/winpty/src/winpty.gyp new file mode 100644 index 0000000000..7ee68d55e6 --- /dev/null +++ b/src/libs/3rdparty/winpty/src/winpty.gyp @@ -0,0 +1,206 @@ +{ + # The MSVC generator is the default. Select the compiler version by + # passing -G msvs_version=<ver> to gyp. <ver> is a string like 2013e. + # See gyp\pylib\gyp\MSVSVersion.py for sample version strings. You + # can also pass configurations.gypi to gyp for 32-bit and 64-bit builds. + # See that file for details. + # + # Pass --format=make to gyp to generate a Makefile instead. The Makefile + # can be configured by passing variables to make, e.g.: + # make -j4 CXX=i686-w64-mingw32-g++ LDFLAGS="-static -static-libgcc -static-libstdc++" + + 'variables': { + 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && GetCommitHash.bat")', + }, + 'target_defaults' : { + 'defines' : [ + 'UNICODE', + '_UNICODE', + '_WIN32_WINNT=0x0501', + 'NOMINMAX', + ], + 'include_dirs': [ + # Add the 'src/gen' directory to the include path and force gyp to + # run the script (re)generating the version header. + '<!(cmd /c "cd shared && UpdateGenVersion.bat <(WINPTY_COMMIT_HASH)")', + ], + }, + 'targets' : [ + { + 'target_name' : 'winpty-agent', + 'type' : 'executable', + 'include_dirs' : [ + 'include', + ], + 'defines' : [ + 'WINPTY_AGENT_ASSERT', + ], + 'libraries' : [ + '-ladvapi32', + '-lshell32', + '-luser32', + ], + 'msvs_settings': { + # Specify this setting here to override a setting from somewhere + # else, such as node's common.gypi. + 'VCCLCompilerTool': { + 'ExceptionHandling': '1', # /EHsc + }, + }, + 'sources' : [ + 'agent/Agent.h', + 'agent/Agent.cc', + 'agent/AgentCreateDesktop.h', + 'agent/AgentCreateDesktop.cc', + 'agent/ConsoleFont.cc', + 'agent/ConsoleFont.h', + 'agent/ConsoleInput.cc', + 'agent/ConsoleInput.h', + 'agent/ConsoleInputReencoding.cc', + 'agent/ConsoleInputReencoding.h', + 'agent/ConsoleLine.cc', + 'agent/ConsoleLine.h', + 'agent/Coord.h', + 'agent/DebugShowInput.h', + 'agent/DebugShowInput.cc', + 'agent/DefaultInputMap.h', + 'agent/DefaultInputMap.cc', + 'agent/DsrSender.h', + 'agent/EventLoop.h', + 'agent/EventLoop.cc', + 'agent/InputMap.h', + 'agent/InputMap.cc', + 'agent/LargeConsoleRead.h', + 'agent/LargeConsoleRead.cc', + 'agent/NamedPipe.h', + 'agent/NamedPipe.cc', + 'agent/Scraper.h', + 'agent/Scraper.cc', + 'agent/SimplePool.h', + 'agent/SmallRect.h', + 'agent/Terminal.h', + 'agent/Terminal.cc', + 'agent/UnicodeEncoding.h', + 'agent/Win32Console.cc', + 'agent/Win32Console.h', + 'agent/Win32ConsoleBuffer.cc', + 'agent/Win32ConsoleBuffer.h', + 'agent/main.cc', + 'shared/AgentMsg.h', + 'shared/BackgroundDesktop.h', + 'shared/BackgroundDesktop.cc', + 'shared/Buffer.h', + 'shared/Buffer.cc', + 'shared/DebugClient.h', + 'shared/DebugClient.cc', + 'shared/GenRandom.h', + 'shared/GenRandom.cc', + 'shared/OsModule.h', + 'shared/OwnedHandle.h', + 'shared/OwnedHandle.cc', + 'shared/StringBuilder.h', + 'shared/StringUtil.cc', + 'shared/StringUtil.h', + 'shared/UnixCtrlChars.h', + 'shared/WindowsSecurity.cc', + 'shared/WindowsSecurity.h', + 'shared/WindowsVersion.h', + 'shared/WindowsVersion.cc', + 'shared/WinptyAssert.h', + 'shared/WinptyAssert.cc', + 'shared/WinptyException.h', + 'shared/WinptyException.cc', + 'shared/WinptyVersion.h', + 'shared/WinptyVersion.cc', + 'shared/winpty_snprintf.h', + ], + }, + { + 'target_name' : 'winpty', + 'type' : 'shared_library', + 'include_dirs' : [ + 'include', + ], + 'defines' : [ + 'COMPILING_WINPTY_DLL', + ], + 'libraries' : [ + '-ladvapi32', + '-luser32', + ], + 'msvs_settings': { + # Specify this setting here to override a setting from somewhere + # else, such as node's common.gypi. + 'VCCLCompilerTool': { + 'ExceptionHandling': '1', # /EHsc + }, + }, + 'sources' : [ + 'include/winpty.h', + 'libwinpty/AgentLocation.cc', + 'libwinpty/AgentLocation.h', + 'libwinpty/winpty.cc', + 'shared/AgentMsg.h', + 'shared/BackgroundDesktop.h', + 'shared/BackgroundDesktop.cc', + 'shared/Buffer.h', + 'shared/Buffer.cc', + 'shared/DebugClient.h', + 'shared/DebugClient.cc', + 'shared/GenRandom.h', + 'shared/GenRandom.cc', + 'shared/OsModule.h', + 'shared/OwnedHandle.h', + 'shared/OwnedHandle.cc', + 'shared/StringBuilder.h', + 'shared/StringUtil.cc', + 'shared/StringUtil.h', + 'shared/WindowsSecurity.cc', + 'shared/WindowsSecurity.h', + 'shared/WindowsVersion.h', + 'shared/WindowsVersion.cc', + 'shared/WinptyAssert.h', + 'shared/WinptyAssert.cc', + 'shared/WinptyException.h', + 'shared/WinptyException.cc', + 'shared/WinptyVersion.h', + 'shared/WinptyVersion.cc', + 'shared/winpty_snprintf.h', + ], + }, + { + 'target_name' : 'winpty-debugserver', + 'type' : 'executable', + 'msvs_settings': { + # Specify this setting here to override a setting from somewhere + # else, such as node's common.gypi. + 'VCCLCompilerTool': { + 'ExceptionHandling': '1', # /EHsc + }, + }, + 'sources' : [ + 'debugserver/DebugServer.cc', + 'shared/DebugClient.h', + 'shared/DebugClient.cc', + 'shared/OwnedHandle.h', + 'shared/OwnedHandle.cc', + 'shared/OsModule.h', + 'shared/StringBuilder.h', + 'shared/StringUtil.cc', + 'shared/StringUtil.h', + 'shared/WindowsSecurity.h', + 'shared/WindowsSecurity.cc', + 'shared/WindowsVersion.h', + 'shared/WindowsVersion.cc', + 'shared/WinptyAssert.h', + 'shared/WinptyAssert.cc', + 'shared/WinptyException.h', + 'shared/WinptyException.cc', + 'shared/winpty_snprintf.h', + ], + 'libraries' : [ + '-ladvapi32', + ], + } + ], +} diff --git a/src/libs/3rdparty/winpty/vcbuild.bat b/src/libs/3rdparty/winpty/vcbuild.bat new file mode 100644 index 0000000000..f3787a20f1 --- /dev/null +++ b/src/libs/3rdparty/winpty/vcbuild.bat @@ -0,0 +1,83 @@ +@echo off + +REM -- Script requirements: +REM -- +REM -- * git This program must be in the Path to check out +REM -- build-gyp. If that directory already exists, then +REM -- git isn't necessary, but if it is missing, no +REM -- commit hash will be embedded into binaries. +REM -- +REM -- * python A non-Cygwin Python 2 python.exe must be in the +REM -- Path to run gyp. +REM -- +REM -- * msbuild msbuild must be in the Path. It is probably +REM -- important to have msbuild from the correct MSVC +REM -- release. +REM -- +REM -- The script's output binaries are in the src/Release/{Win32,x64} +REM -- directory. + +REM ------------------------------------------------------------------------- +REM -- Parse arguments + +setlocal +cd %~dp0 +set GYP_ARGS= +set MSVC_PLATFORM=x64 + +:ParamLoop +if "%1" == "" goto :ParamDone +if "%1" == "--msvc-platform" ( + REM -- One of Win32 or x64. + set MSVC_PLATFORM=%2 + shift && shift + goto :ParamLoop +) +if "%1" == "--gyp-msvs-version" ( + set GYP_ARGS=%GYP_ARGS% -G msvs_version=%2 + shift && shift + goto :ParamLoop +) +if "%1" == "--toolset" ( + set GYP_ARGS=%GYP_ARGS% -D WINPTY_MSBUILD_TOOLSET=%2 + shift && shift + goto :ParamLoop +) +if "%1" == "--commit-hash" ( + set GYP_ARGS=%GYP_ARGS% -D WINPTY_COMMIT_HASH=%2 + shift && shift + goto :ParamLoop +) +echo error: Unrecognized argument: %1 +exit /b 1 +:ParamDone + +REM ------------------------------------------------------------------------- +REM -- Check out GYP. GYP doesn't seem to have releases, so just use the +REM -- current master commit. + +if not exist build-gyp ( + git clone https://chromium.googlesource.com/external/gyp build-gyp || ( + echo error: GYP clone failed + exit /b 1 + ) +) + +REM ------------------------------------------------------------------------- +REM -- Run gyp to generate MSVC project files. + +cd src + +call ..\build-gyp\gyp.bat winpty.gyp -I configurations.gypi %GYP_ARGS% +if errorlevel 1 ( + echo error: GYP failed + exit /b 1 +) + +REM ------------------------------------------------------------------------- +REM -- Compile the project. + +msbuild winpty.sln /m /p:Platform=%MSVC_PLATFORM% || ( + echo error: msbuild failed + exit /b 1 +) diff --git a/src/libs/3rdparty/winpty/winpty.qbs b/src/libs/3rdparty/winpty/winpty.qbs new file mode 100644 index 0000000000..35d56f9265 --- /dev/null +++ b/src/libs/3rdparty/winpty/winpty.qbs @@ -0,0 +1,205 @@ +import qbs +import qbs.TextFile + +Project { + name: "Winpty" + condition: qbs.targetOS.contains("windows") + + Product { + name: "winpty_genversion_header" + type: "hpp" + + Group { + files: "VERSION.txt" + fileTags: "txt.in" + } + + Rule { + inputs: "txt.in" + Artifact { + filePath: "GenVersion.h" + fileTags: "hpp" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating GenVersion.h"; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + var inFile = new TextFile(input.filePath); + var versionTxt = inFile.readAll(); + inFile.close(); + // remove any line endings + versionTxt = versionTxt.replace(/[\r\n]/g, ""); + + var content = 'const char GenVersion_Version[] = "@VERSION@";\n' + + 'const char GenVersion_Commit[] = "@COMMIT_HASH@";\n'; + content = content.replace(/@VERSION@/g, versionTxt); + + var outFile = new TextFile(output.filePath, TextFile.WriteOnly); + outFile.truncate(); + outFile.write(content); + outFile.close(); + } + return cmd; + } + } + + Export { + Depends { name: "cpp" } + cpp.includePaths: exportingProduct.buildDirectory + } + } + + QtcTool { + name: "winpty-agent" + Depends { name: "winpty_genversion_header" } + Depends { name: "cpp" } + + useQt: false + + cpp.includePaths: base.concat([sourceDirectory + "/include", buildDirectory]) + cpp.defines: base.concat(["WINPTY_AGENT_ASSERT", + "NOMINMAX", "UNICODE", "_UNICODE" + ]) + cpp.dynamicLibraries: ["user32", "shell32", "advapi32"] + + files: [ + "src/agent/Agent.h", + "src/agent/Agent.cc", + "src/agent/AgentCreateDesktop.h", + "src/agent/AgentCreateDesktop.cc", + "src/agent/ConsoleFont.cc", + "src/agent/ConsoleFont.h", + "src/agent/ConsoleInput.cc", + "src/agent/ConsoleInput.h", + "src/agent/ConsoleInputReencoding.cc", + "src/agent/ConsoleInputReencoding.h", + "src/agent/ConsoleLine.cc", + "src/agent/ConsoleLine.h", + "src/agent/Coord.h", + "src/agent/DebugShowInput.h", + "src/agent/DebugShowInput.cc", + "src/agent/DefaultInputMap.h", + "src/agent/DefaultInputMap.cc", + "src/agent/DsrSender.h", + "src/agent/EventLoop.h", + "src/agent/EventLoop.cc", + "src/agent/InputMap.h", + "src/agent/InputMap.cc", + "src/agent/LargeConsoleRead.h", + "src/agent/LargeConsoleRead.cc", + "src/agent/NamedPipe.h", + "src/agent/NamedPipe.cc", + "src/agent/Scraper.h", + "src/agent/Scraper.cc", + "src/agent/SimplePool.h", + "src/agent/SmallRect.h", + "src/agent/Terminal.h", + "src/agent/Terminal.cc", + "src/agent/UnicodeEncoding.h", + "src/agent/Win32Console.cc", + "src/agent/Win32Console.h", + "src/agent/Win32ConsoleBuffer.cc", + "src/agent/Win32ConsoleBuffer.h", + "src/agent/main.cc", + ] + + Group { + name: "Shared sources" + prefix: "src/shared/" + files: [ + "AgentMsg.h", + "BackgroundDesktop.h", + "BackgroundDesktop.cc", + "Buffer.h", + "Buffer.cc", + "DebugClient.h", + "DebugClient.cc", + "GenRandom.h", + "GenRandom.cc", + "OsModule.h", + "OwnedHandle.h", + "OwnedHandle.cc", + "StringBuilder.h", + "StringUtil.cc", + "StringUtil.h", + "UnixCtrlChars.h", + "WindowsSecurity.cc", + "WindowsSecurity.h", + "WindowsVersion.h", + "WindowsVersion.cc", + "WinptyAssert.h", + "WinptyAssert.cc", + "WinptyException.h", + "WinptyException.cc", + "WinptyVersion.h", + "WinptyVersion.cc", + "winpty_snprintf.h", + ] + } + } + + QtcLibrary { + name: "winpty" + type: "staticlibrary" + + Depends { name: "winpty_genversion_header" } + Depends { name: "cpp" } + + useNonGuiPchFile: false + useGuiPchFile: false + + cpp.defines: base.concat(["COMPILING_WINPTY_DLL", + "NOMINMAX", "UNICODE", "_UNICODE" + ]) + cpp.dynamicLibraries: ["user32", "shell32", "advapi32"] + cpp.includePaths: base.concat(sourceDirectory + "/src/include") + + + files: [ + "src/libwinpty/AgentLocation.cc", + "src/libwinpty/AgentLocation.h", + "src/libwinpty/winpty.cc", + ] + + Group { + name: "Shared sources" // FIXME duplication + prefix: "src/shared/" + files: [ + "AgentMsg.h", + "BackgroundDesktop.h", + "BackgroundDesktop.cc", + "Buffer.h", + "Buffer.cc", + "DebugClient.h", + "DebugClient.cc", + "GenRandom.h", + "GenRandom.cc", + "OsModule.h", + "OwnedHandle.h", + "OwnedHandle.cc", + "StringBuilder.h", + "StringUtil.cc", + "StringUtil.h", + "UnixCtrlChars.h", + "WindowsSecurity.cc", + "WindowsSecurity.h", + "WindowsVersion.h", + "WindowsVersion.cc", + "WinptyAssert.h", + "WinptyAssert.cc", + "WinptyException.h", + "WinptyException.cc", + "WinptyVersion.h", + "WinptyVersion.cc", + "winpty_snprintf.h", + ] + } + + Export { + Depends { name: "cpp" } + cpp.defines: "COMPILING_WINPTY_DLL" + cpp.includePaths: exportingProduct.sourceDirectory + "/src/include" + } + } +} |