Compare commits
No commits in common. "winpty-cygwin-prebuilts" and "master" have entirely different histories.
winpty-cyg
...
master
19
.gitattributes
vendored
Normal file
19
.gitattributes
vendored
Normal file
@ -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
|
18
.gitignore
vendored
Executable file → Normal file
18
.gitignore
vendored
Executable file → Normal file
@ -1,2 +1,16 @@
|
||||
__pycache__
|
||||
/out
|
||||
*.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
|
||||
|
1
LICENSE
1
LICENSE
@ -1,7 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2016 Ryan Prichard
|
||||
Copyright (c) 2017-2018 Google LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
|
166
Makefile
Normal file
166
Makefile
Normal file
@ -0,0 +1,166 @@
|
||||
# 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.
|
||||
|
||||
# Use make -n to see the actual command-lines make would run.
|
||||
|
||||
# The default "make install" prefix is /usr/local. Pass PREFIX=<path> on the
|
||||
# command-line to override the default.
|
||||
|
||||
.SECONDEXPANSION :
|
||||
|
||||
.PHONY : default
|
||||
default : all
|
||||
|
||||
PREFIX := /usr/local
|
||||
UNIX_ADAPTER_EXE := winpty.exe
|
||||
MINGW_ENABLE_CXX11_FLAG := -std=c++11
|
||||
USE_PCH := 1
|
||||
|
||||
COMMON_CXXFLAGS :=
|
||||
UNIX_CXXFLAGS :=
|
||||
MINGW_CXXFLAGS :=
|
||||
MINGW_LDFLAGS :=
|
||||
UNIX_LDFLAGS :=
|
||||
|
||||
# Include config.mk but complain if it hasn't been created yet.
|
||||
ifeq "$(wildcard config.mk)" ""
|
||||
$(error config.mk does not exist. Please run ./configure)
|
||||
endif
|
||||
include config.mk
|
||||
|
||||
COMMON_CXXFLAGS += \
|
||||
-MMD -Wall \
|
||||
-DUNICODE \
|
||||
-D_UNICODE \
|
||||
-D_WIN32_WINNT=0x0501 \
|
||||
-Ibuild/gen
|
||||
|
||||
UNIX_CXXFLAGS += \
|
||||
$(COMMON_CXXFLAGS)
|
||||
|
||||
MINGW_CXXFLAGS += \
|
||||
$(COMMON_CXXFLAGS) \
|
||||
-O2 \
|
||||
$(MINGW_ENABLE_CXX11_FLAG)
|
||||
|
||||
MINGW_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||
UNIX_LDFLAGS += $(UNIX_LDFLAGS_STATIC)
|
||||
|
||||
ifeq "$(USE_PCH)" "1"
|
||||
MINGW_CXXFLAGS += -include build/mingw/PrecompiledHeader.h
|
||||
PCH_DEP := build/mingw/PrecompiledHeader.h.gch
|
||||
else
|
||||
PCH_DEP :=
|
||||
endif
|
||||
|
||||
build/gen/GenVersion.h : VERSION.txt $(COMMIT_HASH_DEP) | $$(@D)/.mkdir
|
||||
$(info Updating build/gen/GenVersion.h)
|
||||
@echo "const char GenVersion_Version[] = \"$(shell cat VERSION.txt | tr -d '\r\n')\";" > build/gen/GenVersion.h
|
||||
@echo "const char GenVersion_Commit[] = \"$(COMMIT_HASH)\";" >> build/gen/GenVersion.h
|
||||
|
||||
build/mingw/PrecompiledHeader.h : src/shared/PrecompiledHeader.h | $$(@D)/.mkdir
|
||||
$(info Copying $< to $@)
|
||||
@cp $< $@
|
||||
|
||||
build/mingw/PrecompiledHeader.h.gch : build/mingw/PrecompiledHeader.h | $$(@D)/.mkdir
|
||||
$(info Compiling $<)
|
||||
@$(MINGW_CXX) $(MINGW_CXXFLAGS) -c -o $@ $<
|
||||
|
||||
-include build/mingw/PrecompiledHeader.h.d
|
||||
|
||||
define def_unix_target
|
||||
build/$1/%.o : src/%.cc | $$$$(@D)/.mkdir
|
||||
$$(info Compiling $$<)
|
||||
@$$(UNIX_CXX) $$(UNIX_CXXFLAGS) $2 -I src/include -c -o $$@ $$<
|
||||
endef
|
||||
|
||||
define def_mingw_target
|
||||
build/$1/%.o : src/%.cc $$(PCH_DEP) | $$$$(@D)/.mkdir
|
||||
$$(info Compiling $$<)
|
||||
@$$(MINGW_CXX) $$(MINGW_CXXFLAGS) $2 -I src/include -c -o $$@ $$<
|
||||
endef
|
||||
|
||||
include src/subdir.mk
|
||||
|
||||
.PHONY : all
|
||||
all : $(ALL_TARGETS)
|
||||
|
||||
.PHONY : tests
|
||||
tests : $(TEST_PROGRAMS)
|
||||
|
||||
.PHONY : install-bin
|
||||
install-bin : all
|
||||
mkdir -p $(PREFIX)/bin
|
||||
install -m 755 -p -s build/$(UNIX_ADAPTER_EXE) $(PREFIX)/bin
|
||||
install -m 755 -p -s build/winpty.dll $(PREFIX)/bin
|
||||
install -m 755 -p -s build/winpty-agent.exe $(PREFIX)/bin
|
||||
|
||||
.PHONY : install-debugserver
|
||||
install-debugserver : all
|
||||
mkdir -p $(PREFIX)/bin
|
||||
install -m 755 -p -s build/winpty-debugserver.exe $(PREFIX)/bin
|
||||
|
||||
.PHONY : install-lib
|
||||
install-lib : all
|
||||
mkdir -p $(PREFIX)/lib
|
||||
install -m 644 -p build/winpty.lib $(PREFIX)/lib
|
||||
|
||||
.PHONY : install-doc
|
||||
install-doc :
|
||||
mkdir -p $(PREFIX)/share/doc/winpty
|
||||
install -m 644 -p LICENSE $(PREFIX)/share/doc/winpty
|
||||
install -m 644 -p README.md $(PREFIX)/share/doc/winpty
|
||||
install -m 644 -p RELEASES.md $(PREFIX)/share/doc/winpty
|
||||
|
||||
.PHONY : install-include
|
||||
install-include :
|
||||
mkdir -p $(PREFIX)/include/winpty
|
||||
install -m 644 -p src/include/winpty.h $(PREFIX)/include/winpty
|
||||
install -m 644 -p src/include/winpty_constants.h $(PREFIX)/include/winpty
|
||||
|
||||
.PHONY : install
|
||||
install : \
|
||||
install-bin \
|
||||
install-debugserver \
|
||||
install-lib \
|
||||
install-doc \
|
||||
install-include
|
||||
|
||||
.PHONY : clean
|
||||
clean :
|
||||
rm -fr build
|
||||
|
||||
.PHONY : clean-msvc
|
||||
clean-msvc :
|
||||
rm -fr src/Default src/Release src/.vs src/gen
|
||||
rm -f src/*.vcxproj src/*.vcxproj.filters src/*.sln src/*.sdf
|
||||
|
||||
.PHONY : distclean
|
||||
distclean : clean
|
||||
rm -f config.mk
|
||||
|
||||
.PRECIOUS : %.mkdir
|
||||
%.mkdir :
|
||||
$(info Creating directory $(dir $@))
|
||||
@mkdir -p $(dir $@)
|
||||
@touch $@
|
||||
|
||||
src/%.h :
|
||||
@echo "Missing header file $@ (stale dependency file?)"
|
151
README.md
Normal file
151
README.md
Normal file
@ -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.
|
280
RELEASES.md
Normal file
280
RELEASES.md
Normal file
@ -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.
|
1
VERSION.txt
Normal file
1
VERSION.txt
Normal file
@ -0,0 +1 @@
|
||||
0.4.4-dev
|
16
appveyor.yml
16
appveyor.yml
@ -1,6 +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:\Python37-x64\python.exe make-cygwin-prebuilts.py
|
||||
- C:\Python37-x64\python.exe make-msys2-prebuilts.py
|
||||
- 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: out\artifact\*.7z
|
||||
- path: ship\packages\*.tar.gz
|
||||
- path: ship\packages\*.zip
|
||||
|
167
configure
vendored
Executable file
167
configure
vendored
Executable file
@ -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
|
@ -1,20 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def versionDict(dllpath):
|
||||
if not os.path.isfile(dllpath):
|
||||
sys.exit('error: {} is not an existing file'.format(dllpath))
|
||||
txt = subprocess.check_output([
|
||||
'powershell',
|
||||
'[System.Diagnostics.FileVersionInfo]::GetVersionInfo("{}") | ConvertTo-Json'.format(dllpath.replace('\\', '/')),
|
||||
])
|
||||
return json.loads(txt)
|
||||
|
||||
|
||||
def fileVersion(dllpath):
|
||||
ret = versionDict(dllpath)['FileVersion']
|
||||
assert ret not in (None, '')
|
||||
return ret
|
@ -1,76 +0,0 @@
|
||||
#!python3
|
||||
import os
|
||||
import sys
|
||||
import util
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import dllversion
|
||||
|
||||
from os.path import abspath
|
||||
from subprocess import check_call
|
||||
from util import glob_paths, rmpath, mkdirs, buildTimeStamp, projectDir, getGppVer
|
||||
|
||||
|
||||
sys.platform == 'win32' or sys.exit('error: script only runs on Windows (no Cygwin/MSYS)')
|
||||
shutil.which('7z') or sys.exit('error: 7z missing')
|
||||
shutil.which('curl') or sys.exit('error: curl missing')
|
||||
|
||||
|
||||
buildDir = os.path.join(projectDir, 'out\\build-cygwin')
|
||||
artifactDir = os.path.join(projectDir, 'out\\artifact')
|
||||
rmpath(buildDir)
|
||||
mkdirs(buildDir)
|
||||
mkdirs(artifactDir)
|
||||
|
||||
os.chdir(buildDir)
|
||||
|
||||
for setup, cygwin in (('setup-x86_64', 'cygwin64'), ('setup-x86', 'cygwin32')):
|
||||
|
||||
check_call(['curl', '-fL', '-O', 'https://cygwin.com/{}.exe'.format(setup)])
|
||||
|
||||
# Include cross-compilers targeting both architectures, in both packages,
|
||||
# because I might want to require having both cross-compilers in the build
|
||||
# system at some point. (e.g. if winpty includes a synchronous WinEvents
|
||||
# hook loaded into conhost.exe)
|
||||
cygwinPackages = [
|
||||
'gcc-g++',
|
||||
'make',
|
||||
'mingw64-i686-gcc-g++',
|
||||
'mingw64-x86_64-gcc-g++',
|
||||
]
|
||||
check_call([
|
||||
abspath('{}.exe'.format(setup)),
|
||||
'-l', abspath('{}-packages'.format(cygwin)),
|
||||
'-P', ','.join(cygwinPackages),
|
||||
'-s', 'http://mirrors.kernel.org/sourceware/cygwin',
|
||||
'-R', abspath(cygwin),
|
||||
'--no-admin', '--no-desktop', '--no-shortcuts', '--no-startmenu', '--quiet-mode',
|
||||
])
|
||||
|
||||
check_call(['{}/bin/ash.exe'.format(cygwin), '/bin/rebaseall', '-v'])
|
||||
|
||||
dllVer = dllversion.fileVersion('{}/bin/cygwin1.dll'.format(cygwin))
|
||||
cygGccVer = getGppVer(['{}/bin/g++.exe'.format(cygwin), '--version'])
|
||||
winGccVer = getGppVer(['{}/bin/x86_64-w64-mingw32-g++.exe'.format(cygwin), '--version'])
|
||||
filename = '{}\\{}-{}-dll{}-cyggcc{}-wingcc{}.7z'.format(
|
||||
artifactDir, cygwin, buildTimeStamp, dllVer, cygGccVer, winGccVer)
|
||||
rmpath(filename)
|
||||
|
||||
open(cygwin + '/tmp/.keep', 'wb').close()
|
||||
|
||||
check_call(['7z', 'a', '-mx=9', filename] + glob_paths([
|
||||
cygwin + '/dev',
|
||||
cygwin + '/etc/setup',
|
||||
cygwin + '/tmp/.keep',
|
||||
cygwin + '/bin',
|
||||
cygwin + '/lib',
|
||||
cygwin + '/usr/include',
|
||||
cygwin + '/usr/share/doc',
|
||||
cygwin + '/usr/share/info',
|
||||
cygwin + '/usr/share/man',
|
||||
cygwin + '/usr/*-pc-cygwin',
|
||||
cygwin + '/usr/*-w64-mingw32',
|
||||
]))
|
@ -1,124 +0,0 @@
|
||||
#!python3
|
||||
import os
|
||||
import sys
|
||||
import util
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import urllib
|
||||
|
||||
import dllversion
|
||||
|
||||
from os.path import abspath
|
||||
from subprocess import check_call
|
||||
from util import glob_paths, rmpath, mkdirs, buildTimeStamp, projectDir, getGppVer
|
||||
|
||||
|
||||
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))
|
||||
|
||||
|
||||
sys.platform == 'win32' or sys.exit('error: script only runs on Windows (no Cygwin/MSYS)')
|
||||
shutil.which('7z') or sys.exit('error: 7z missing')
|
||||
shutil.which('curl') or sys.exit('error: curl missing')
|
||||
|
||||
buildDir = os.path.join(projectDir, 'out\\build-msys2')
|
||||
artifactDir = os.path.join(projectDir, 'out\\artifact')
|
||||
rmpath(buildDir)
|
||||
mkdirs(buildDir)
|
||||
mkdirs(artifactDir)
|
||||
|
||||
os.chdir(buildDir)
|
||||
|
||||
check_call(['curl', '-fL', '-O', 'http://repo.msys2.org/distrib/i686/msys2-base-i686-20180531.tar.xz'])
|
||||
check_call(['curl', '-fL', '-O', 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz'])
|
||||
checkSha256('msys2-base-i686-20180531.tar.xz', '8ef5b18c4c91f3f2394823f1981babdee78a945836b2625f091ec934b1a37d32')
|
||||
checkSha256('msys2-base-x86_64-20180531.tar.xz', '4e799b5c3efcf9efcb84923656b7bcff16f75a666911abd6620ea8e5e1e9870c')
|
||||
|
||||
for name, arch in (('msys64', 'x86_64'), ('msys32', 'i686')):
|
||||
|
||||
baseArchive = 'msys2-base-{}-20180531'.format(arch)
|
||||
check_call(['7z', 'x', '{}.tar.xz'.format(baseArchive)])
|
||||
check_call(['7z', 'x', '{}.tar'.format(baseArchive)])
|
||||
|
||||
bashPath = abspath(name + '\\usr\\bin\\bash.exe')
|
||||
|
||||
check_call([bashPath, '--login', '-c', 'exit'])
|
||||
good = False
|
||||
for i in range(5):
|
||||
# Apparently in the base install, the 'msys-runtime' and 'catgets'
|
||||
# packages are incompatible, and passing --ask=20 confirms to MSYS2
|
||||
# that we should do the necessary thing (remove catgets, I guess?)
|
||||
# See https://github.com/Alexpux/MSYS2-packages/issues/1141.
|
||||
cmd = [bashPath, '--login', '-c', 'pacman --ask=20 --noconfirm -Syuu']
|
||||
print('Running {} ...'.format(repr(cmd)))
|
||||
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8')
|
||||
sys.stdout.write(p.stdout)
|
||||
if p.returncode != 0:
|
||||
sys.exit('error: MSYS2 system update failed')
|
||||
|
||||
good = 'there is nothing to do' in [x.strip() for x in p.stdout.splitlines()]
|
||||
|
||||
good or sys.exit('error: MSYS2 system update never finished')
|
||||
|
||||
# Include cross-compilers targeting both architectures, in both packages,
|
||||
# because I might want to require having both cross-compilers in the build
|
||||
# system at some point. (e.g. if winpty includes a synchronous WinEvents
|
||||
# hook loaded into conhost.exe)
|
||||
msysPackages = [
|
||||
'msys/gcc',
|
||||
'msys/make',
|
||||
'msys/tar',
|
||||
'mingw-w64-cross-toolchain',
|
||||
'python3',
|
||||
]
|
||||
cmd = [bashPath, '--login', '-c', 'pacman --noconfirm -S ' + ' '.join(msysPackages)]
|
||||
print('Running {} ...'.format(repr(cmd)))
|
||||
check_call(cmd)
|
||||
|
||||
# pacman/gpg start gpg-agent and scdaemon processes, which prevents
|
||||
# rebasing and would break appveyor. Kill pacman's gpg-agent.
|
||||
# https://help.appveyor.com/discussions/problems/16396-build-freezes-between-commands-in-appveyoryml
|
||||
check_call([
|
||||
'{}/usr/bin/gpgconf.exe'.format(name),
|
||||
'--homedir', '/etc/pacman.d/gnupg',
|
||||
'--kill', 'all',
|
||||
])
|
||||
|
||||
# The -p option passed by autorebase.bat doesn't look necessary. It relaxes
|
||||
# the sanity checking to allow more than just ash.exe/dash.exe processes.
|
||||
check_call(['{}/usr/bin/ash.exe'.format(name), '/usr/bin/rebaseall', '-v'])
|
||||
|
||||
dllVer = dllversion.fileVersion('{}/usr/bin/msys-2.0.dll'.format(name))
|
||||
msysGccVer = getGppVer([bashPath, '--login', '-c', '/usr/bin/g++.exe --version'])
|
||||
winGccVer = getGppVer([bashPath, '--login', '-c', '/opt/bin/x86_64-w64-mingw32-g++.exe --version'])
|
||||
filename = '{}\\{}-{}-dll{}-msysgcc{}-wingcc{}.7z'.format(
|
||||
artifactDir, name, buildTimeStamp, dllVer, msysGccVer, winGccVer)
|
||||
rmpath(filename)
|
||||
|
||||
open(name + '/tmp/.keep', 'wb').close()
|
||||
open(name + '/etc/.keep', 'wb').close()
|
||||
|
||||
# Use pacman to determine a list of files to package.
|
||||
cmd = [bashPath, '--login', '-c', 'python3 {}/msys2_subset_files.py'.format(projectDir.replace('\\', '/'))]
|
||||
paths = subprocess.check_output(cmd).decode().splitlines()
|
||||
paths.extend([
|
||||
'dev',
|
||||
'etc/.keep',
|
||||
'tmp/.keep',
|
||||
])
|
||||
paths = ['{}/{}'.format(name, p) for p in paths]
|
||||
listFile = '{}-files'.format(name)
|
||||
with open(listFile, 'w') as fp:
|
||||
fp.write('\n'.join(paths) + '\n')
|
||||
|
||||
check_call(['7z', 'a', '-mx=9', filename, '@' + listFile])
|
2
misc/.gitignore
vendored
Normal file
2
misc/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.exe
|
||||
UnixEcho
|
90
misc/BufferResizeTests.cc
Normal file
90
misc/BufferResizeTests.cc
Normal file
@ -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;
|
||||
}
|
53
misc/ChangeScreenBuffer.cc
Normal file
53
misc/ChangeScreenBuffer.cc
Normal file
@ -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;
|
||||
}
|
72
misc/ClearConsole.cc
Normal file
72
misc/ClearConsole.cc
Normal file
@ -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);
|
||||
}
|
117
misc/ConinMode.cc
Executable file
117
misc/ConinMode.cc
Executable file
@ -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;
|
||||
}
|
116
misc/ConinMode.ps1
Executable file
116
misc/ConinMode.ps1
Executable file
@ -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"
|
||||
}
|
113
misc/ConoutMode.cc
Executable file
113
misc/ConoutMode.cc
Executable file
@ -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;
|
||||
}
|
42
misc/DebugClient.py
Executable file
42
misc/DebugClient.py
Executable file
@ -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)
|
63
misc/DebugServer.py
Executable file
63
misc/DebugServer.py
Executable file
@ -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)
|
5
misc/DumpLines.py
Executable file
5
misc/DumpLines.py
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
for i in range(1, int(sys.argv[1]) + 1):
|
||||
print i, "X" * 78
|
46
misc/EnableExtendedFlags.txt
Executable file
46
misc/EnableExtendedFlags.txt
Executable file
@ -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.
|
528
misc/Font-Report-June2016/CP437-Consolas.txt
Normal file
528
misc/Font-Report-June2016/CP437-Consolas.txt
Normal file
@ -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)
|
633
misc/Font-Report-June2016/CP437-Lucida.txt
Normal file
633
misc/Font-Report-June2016/CP437-Lucida.txt
Normal file
@ -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)
|
630
misc/Font-Report-June2016/CP932.txt
Normal file
630
misc/Font-Report-June2016/CP932.txt
Normal file
@ -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)
|
630
misc/Font-Report-June2016/CP936.txt
Normal file
630
misc/Font-Report-June2016/CP936.txt
Normal file
@ -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)
|
630
misc/Font-Report-June2016/CP949.txt
Normal file
630
misc/Font-Report-June2016/CP949.txt
Normal file
@ -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)
|
630
misc/Font-Report-June2016/CP950.txt
Normal file
630
misc/Font-Report-June2016/CP950.txt
Normal file
@ -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)
|
16
misc/Font-Report-June2016/MinimumWindowWidths.txt
Normal file
16
misc/Font-Report-June2016/MinimumWindowWidths.txt
Normal file
@ -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.
|
4
misc/Font-Report-June2016/Results.txt
Normal file
4
misc/Font-Report-June2016/Results.txt
Normal file
@ -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.
|
144
misc/Font-Report-June2016/Windows10SetFontBugginess.txt
Normal file
144
misc/Font-Report-June2016/Windows10SetFontBugginess.txt
Normal file
@ -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.
|
100
misc/FontSurvey.cc
Executable file
100
misc/FontSurvey.cc
Executable file
@ -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);
|
||||
}
|
21
misc/FormatChar.h
Normal file
21
misc/FormatChar.h
Normal file
@ -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;
|
||||
}
|
||||
}
|
62
misc/FreezePerfTest.cc
Normal file
62
misc/FreezePerfTest.cc
Normal file
@ -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;
|
||||
}
|
20
misc/GetCh.cc
Executable file
20
misc/GetCh.cc
Executable file
@ -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;
|
||||
}
|
41
misc/GetConsolePos.cc
Normal file
41
misc/GetConsolePos.cc
Normal file
@ -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;
|
||||
}
|
261
misc/GetFont.cc
Normal file
261
misc/GetFont.cc
Normal file
@ -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;
|
||||
}
|
51
misc/IdentifyConsoleWindow.ps1
Executable file
51
misc/IdentifyConsoleWindow.ps1
Executable file
@ -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?)"
|
||||
}
|
||||
}
|
87
misc/IsNewConsole.cc
Normal file
87
misc/IsNewConsole.cc
Normal file
@ -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;
|
||||
}
|
90
misc/MouseInputNotes.txt
Normal file
90
misc/MouseInputNotes.txt
Normal file
@ -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.
|
34
misc/MoveConsoleWindow.cc
Normal file
34
misc/MoveConsoleWindow.cc
Normal file
@ -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;
|
||||
}
|
219
misc/Notes.txt
Normal file
219
misc/Notes.txt
Normal file
@ -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).
|
27
misc/OSVersion.cc
Normal file
27
misc/OSVersion.cc
Normal file
@ -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;
|
||||
}
|
101
misc/ScreenBufferFreezeInactive.cc
Normal file
101
misc/ScreenBufferFreezeInactive.cc
Normal file
@ -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;
|
||||
}
|
671
misc/ScreenBufferTest.cc
Normal file
671
misc/ScreenBufferTest.cc
Normal file
@ -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;
|
||||
}
|
151
misc/ScreenBufferTest2.cc
Normal file
151
misc/ScreenBufferTest2.cc
Normal file
@ -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;
|
||||
}
|
45
misc/SelectAllTest.cc
Normal file
45
misc/SelectAllTest.cc
Normal file
@ -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;
|
||||
}
|
90
misc/SetBufInfo.cc
Executable file
90
misc/SetBufInfo.cc
Executable file
@ -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;
|
||||
}
|
32
misc/SetBufferSize.cc
Normal file
32
misc/SetBufferSize.cc
Normal file
@ -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;
|
||||
}
|
10
misc/SetCursorPos.cc
Normal file
10
misc/SetCursorPos.cc
Normal file
@ -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;
|
||||
}
|
145
misc/SetFont.cc
Normal file
145
misc/SetFont.cc
Normal file
@ -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;
|
||||
}
|
36
misc/SetWindowRect.cc
Normal file
36
misc/SetWindowRect.cc
Normal file
@ -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;
|
||||
}
|
12
misc/ShowArgv.cc
Normal file
12
misc/ShowArgv.cc
Normal file
@ -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;
|
||||
}
|
40
misc/ShowConsoleInput.cc
Normal file
40
misc/ShowConsoleInput.cc
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
5
misc/Spew.py
Executable file
5
misc/Spew.py
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
i = 0;
|
||||
while True:
|
||||
i += 1
|
||||
print(i)
|
172
misc/TestUtil.cc
Normal file
172
misc/TestUtil.cc
Normal file
@ -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;
|
||||
}
|
102
misc/UnicodeDoubleWidthTest.cc
Normal file
102
misc/UnicodeDoubleWidthTest.cc
Normal file
@ -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;
|
||||
}
|
246
misc/UnicodeWideTest1.cc
Normal file
246
misc/UnicodeWideTest1.cc
Normal file
@ -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;
|
||||
}
|
130
misc/UnicodeWideTest2.cc
Normal file
130
misc/UnicodeWideTest2.cc
Normal file
@ -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;
|
||||
}
|
89
misc/UnixEcho.cc
Normal file
89
misc/UnixEcho.cc
Normal file
@ -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;
|
||||
}
|
46
misc/Utf16Echo.cc
Normal file
46
misc/Utf16Echo.cc
Normal file
@ -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;
|
||||
}
|
122
misc/VeryLargeRead.cc
Normal file
122
misc/VeryLargeRead.cc
Normal file
@ -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;
|
||||
}
|
56
misc/VkEscapeTest.cc
Normal file
56
misc/VkEscapeTest.cc
Normal file
@ -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;
|
||||
}
|
52
misc/Win10ResizeWhileFrozen.cc
Normal file
52
misc/Win10ResizeWhileFrozen.cc
Normal file
@ -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);
|
||||
}
|
57
misc/Win10WrapTest1.cc
Normal file
57
misc/Win10WrapTest1.cc
Normal file
@ -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); }
|
||||
}
|
30
misc/Win10WrapTest2.cc
Normal file
30
misc/Win10WrapTest2.cc
Normal file
@ -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);
|
||||
}
|
26
misc/Win32Echo1.cc
Normal file
26
misc/Win32Echo1.cc
Normal file
@ -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;
|
||||
}
|
19
misc/Win32Echo2.cc
Normal file
19
misc/Win32Echo2.cc
Normal file
@ -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;
|
||||
}
|
46
misc/Win32Test1.cc
Normal file
46
misc/Win32Test1.cc
Normal file
@ -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;
|
||||
}
|
70
misc/Win32Test2.cc
Normal file
70
misc/Win32Test2.cc
Normal file
@ -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;
|
||||
}
|
78
misc/Win32Test3.cc
Normal file
78
misc/Win32Test3.cc
Normal file
@ -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;
|
||||
}
|
44
misc/Win32Write1.cc
Normal file
44
misc/Win32Write1.cc
Normal file
@ -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;
|
||||
}
|
27
misc/WindowsBugCrashReader.cc
Normal file
27
misc/WindowsBugCrashReader.cc
Normal file
@ -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;
|
||||
}
|
106
misc/WriteConsole.cc
Executable file
106
misc/WriteConsole.cc
Executable file
@ -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;
|
||||
}
|
9
misc/build32.sh
Executable file
9
misc/build32.sh
Executable file
@ -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
|
9
misc/build64.sh
Executable file
9
misc/build64.sh
Executable file
@ -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
|
212
misc/color-test.sh
Executable file
212
misc/color-test.sh
Executable file
@ -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
|
300
misc/font-notes.txt
Normal file
300
misc/font-notes.txt
Normal file
@ -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.
|
201
misc/winbug-15048.cc
Normal file
201
misc/winbug-15048.cc
Normal file
@ -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;
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
groups = set()
|
||||
packages = {
|
||||
# Explicit packages
|
||||
'gcc',
|
||||
'make',
|
||||
'tar',
|
||||
'mingw-w64-cross-toolchain',
|
||||
# Implicit packages
|
||||
'bzip2',
|
||||
'coreutils',
|
||||
'findutils', # needed by rebase script
|
||||
'gawk', # needed by rebase script
|
||||
'gzip',
|
||||
'rebase',
|
||||
'which',
|
||||
'xz',
|
||||
}
|
||||
|
||||
# mingw-w64-cross-toolchain is a "group" rather than a package, so expand
|
||||
# it to a list of packages.
|
||||
for line in subprocess.check_output(['pacman', '-Qg']).decode().splitlines():
|
||||
group, member = line.split()
|
||||
if group in packages:
|
||||
groups.add(group)
|
||||
packages.add(member)
|
||||
packages -= groups
|
||||
|
||||
# Transitive closure of dependencies
|
||||
for p in sorted(packages):
|
||||
packages.update(subprocess.check_output(['pactree', '-l', p]).decode().splitlines())
|
||||
|
||||
# Get a list of files
|
||||
paths = set()
|
||||
for line in subprocess.check_output(['pacman', '-Ql']).decode().splitlines():
|
||||
package, path = line.split(' ', 1)
|
||||
if package in packages:
|
||||
paths.add(path)
|
||||
|
||||
# Strip out some unneeded files
|
||||
unneeded = [
|
||||
r'.*/$', # strip out directories
|
||||
r'/opt/armv7-w64-mingw32/',
|
||||
r'/usr/bin/msys-2\.0\.dbg$',
|
||||
r'/(usr|opt)/share/locale/',
|
||||
r'/usr/(share|lib)/terminfo/',
|
||||
]
|
||||
unneeded_re = re.compile('(' + '|'.join(unneeded) + ')')
|
||||
|
||||
paths = {p for p in paths if not unneeded_re.match(p)}
|
||||
|
||||
for x in sorted(paths):
|
||||
assert x[0] == '/'
|
||||
print(x[1:])
|
36
ship/build-pty4j-libpty.bat
Executable file
36
ship/build-pty4j-libpty.bat
Executable file
@ -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
|
89
ship/common_ship.py
Normal file
89
ship/common_ship.py
Normal file
@ -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", [])
|
163
ship/make_msvc_package.py
Executable file
163
ship/make_msvc_package.py
Executable file
@ -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()
|
104
ship/ship.py
Executable file
104
ship/ship.py
Executable file
@ -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()
|
612
src/agent/Agent.cc
Normal file
612
src/agent/Agent.cc
Normal file
@ -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;
|
||||
}
|
||||
}
|
103
src/agent/Agent.h
Normal file
103
src/agent/Agent.h
Normal file
@ -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
|
84
src/agent/AgentCreateDesktop.cc
Executable file
84
src/agent/AgentCreateDesktop.cc
Executable file
@ -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());
|
||||
}
|
||||
}
|
28
src/agent/AgentCreateDesktop.h
Executable file
28
src/agent/AgentCreateDesktop.h
Executable file
@ -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
|
698
src/agent/ConsoleFont.cc
Normal file
698
src/agent/ConsoleFont.cc
Normal file
@ -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: ");
|
||||
}
|
28
src/agent/ConsoleFont.h
Normal file
28
src/agent/ConsoleFont.h
Normal file
@ -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
|
852
src/agent/ConsoleInput.cc
Normal file
852
src/agent/ConsoleInput.cc
Normal file
@ -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;
|
||||
}
|
109
src/agent/ConsoleInput.h
Normal file
109
src/agent/ConsoleInput.h
Normal file
@ -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
|
121
src/agent/ConsoleInputReencoding.cc
Normal file
121
src/agent/ConsoleInputReencoding.cc
Normal file
@ -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);
|
||||
}
|
||||
}
|
36
src/agent/ConsoleInputReencoding.h
Normal file
36
src/agent/ConsoleInputReencoding.h
Normal file
@ -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
|
152
src/agent/ConsoleLine.cc
Normal file
152
src/agent/ConsoleLine.cc
Normal file
@ -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;
|
||||
}
|
41
src/agent/ConsoleLine.h
Normal file
41
src/agent/ConsoleLine.h
Normal file
@ -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
|
87
src/agent/Coord.h
Normal file
87
src/agent/Coord.h
Normal file
@ -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
|
239
src/agent/DebugShowInput.cc
Normal file
239
src/agent/DebugShowInput.cc
Normal file
@ -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);
|
||||
}
|
32
src/agent/DebugShowInput.h
Normal file
32
src/agent/DebugShowInput.h
Normal file
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user