cling/lib/UserInterface/textinput/TerminalDisplayWin.cpp

218 lines
5.9 KiB
C++

//===--- TerminalDisplayWin.h - Output To Windows Console -------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the interface for writing to a Windows console
// i.e. cmd.exe.
//
// Axel Naumann <axel@cern.ch>, 2011-05-12
//===----------------------------------------------------------------------===//
#ifdef WIN32
#include "textinput/TerminalDisplayWin.h"
#include "textinput/Color.h"
namespace textinput {
TerminalDisplayWin::TerminalDisplayWin():
TerminalDisplay(false), fStartLine(0), fIsAttached(false),
fDefaultAttributes(0)
{
fOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
bool isConsole = ::GetConsoleMode(fOut, &fOldMode) != 0;
SetIsTTY(isConsole);
if (isConsole) {
// Prevent redirection from stealing our console handle,
// simply open our own.
fOut = ::CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
::GetConsoleMode(fOut, &fOldMode);
fMyMode = fOldMode | ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
CONSOLE_SCREEN_BUFFER_INFO csbi;
::GetConsoleScreenBufferInfo(fOut, &csbi);
fDefaultAttributes = csbi.wAttributes;
}
HandleResizeEvent();
}
TerminalDisplayWin::~TerminalDisplayWin() {
if (IsTTY()) {
::SetConsoleTextAttribute(fOut, fDefaultAttributes);
// We allocated CONOUT$:
CloseHandle(fOut);
}
}
void
TerminalDisplayWin::HandleResizeEvent() {
if (IsTTY()) {
CONSOLE_SCREEN_BUFFER_INFO Info;
if (!::GetConsoleScreenBufferInfo(fOut, &Info)) {
ShowError("resize / getting console info");
return;
}
SetWidth(Info.dwSize.X);
}
}
void
TerminalDisplayWin::SetColor(char CIdx, const Color& C) {
WORD Attribs = 0;
// There is no underline since DOS has died.
if (C.fModifiers & Color::kModUnderline) Attribs |= BACKGROUND_INTENSITY;
if (C.fModifiers & Color::kModBold) Attribs |= FOREGROUND_INTENSITY;
if (C.fR > 64) Attribs |= FOREGROUND_RED;
if (C.fG > 64) Attribs |= FOREGROUND_GREEN;
if (C.fB > 64) Attribs |= FOREGROUND_BLUE;
::SetConsoleTextAttribute(fOut, Attribs);
}
void
TerminalDisplayWin::CheckCursorPos() {
// Did something print something on the screen?
// I.e. did the cursor move?
CONSOLE_SCREEN_BUFFER_INFO CSI;
if (::GetConsoleScreenBufferInfo(fOut, &CSI)) {
if (CSI.dwCursorPosition.X != fWritePos.fCol
|| CSI.dwCursorPosition.Y != fWritePos.fLine + fStartLine) {
fStartLine = CSI.dwCursorPosition.Y;
if (CSI.dwCursorPosition.X) {
// fStartLine may be a couple of lines higher (or more precisely
// the number of written lines higher)
fStartLine -= fWritePos.fLine;
}
fWritePos.fCol = 0;
fWritePos.fLine = 0;
}
}
}
void
TerminalDisplayWin::Move(Pos P) {
CheckCursorPos();
MoveInternal(P);
fWritePos = P;
}
void
TerminalDisplayWin::MoveInternal(Pos P) {
COORD C = {P.fCol, P.fLine + fStartLine};
::SetConsoleCursorPosition(fOut, C);
}
void
TerminalDisplayWin::MoveFront() {
Pos P(fWritePos);
P.fCol = 0;
MoveInternal(P);
}
void
TerminalDisplayWin::MoveUp(size_t nLines /* = 1 */) {
Pos P(fWritePos);
--P.fLine;
MoveInternal(P);
}
void
TerminalDisplayWin::MoveDown(size_t nLines /* = 1 */) {
Pos P(fWritePos);
++P.fLine;
MoveInternal(P);
}
void
TerminalDisplayWin::MoveRight(size_t nCols /* = 1 */) {
Pos P(fWritePos);
++P.fCol;
MoveInternal(P);
}
void
TerminalDisplayWin::MoveLeft(size_t nCols /* = 1 */) {
Pos P(fWritePos);
--P.fCol;
MoveInternal(P);
}
void
TerminalDisplayWin::EraseToRight() {
DWORD NumWritten;
COORD C = {fWritePos.fCol, fWritePos.fLine + fStartLine};
::FillConsoleOutputCharacter(fOut, ' ', GetWidth() - C.X, C,
&NumWritten);
// It wraps, so move up and reset WritePos:
//MoveUp();
//++WritePos.Line;
}
void
TerminalDisplayWin::WriteRawString(const char *text, size_t len) {
DWORD NumWritten = 0;
if (IsTTY()) {
WriteConsole(fOut, text, (DWORD) len, &NumWritten, NULL);
} else {
WriteFile(fOut, text, (DWORD) len, &NumWritten, NULL);
}
if (NumWritten != len) {
ShowError("writing to output");
}
}
void
TerminalDisplayWin::Attach() {
// set to noecho
if (fIsAttached) return;
if (IsTTY() && !::SetConsoleMode(fOut, fMyMode)) {
ShowError("attaching to console output");
}
CONSOLE_SCREEN_BUFFER_INFO Info;
if (IsTTY()) {
if (!::GetConsoleScreenBufferInfo(fOut, &Info)) {
ShowError("attaching / getting console info");
} else {
fStartLine = Info.dwCursorPosition.Y;
if (Info.dwCursorPosition.X) {
// Whooa - where are we?! Newline and cross fingers:
WriteRawString("\n", 1);
++fStartLine;
}
}
}
fIsAttached = true;
}
void
TerminalDisplayWin::Detach() {
if (!fIsAttached) return;
if (IsTTY() && !SetConsoleMode(fOut, fOldMode)) {
ShowError("detaching to console output");
}
TerminalDisplay::Detach();
fIsAttached = false;
}
void
TerminalDisplayWin::ShowError(const char* Where) const {
DWORD Err = GetLastError();
LPVOID MsgBuf = 0;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, Err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &MsgBuf, 0, NULL);
printf("Error %d in textinput::TerminalDisplayWin %s: %s\n", Err, Where, MsgBuf);
LocalFree(MsgBuf);
}
}
#endif // WIN32