diff -r 000000000000 -r 7f656887cf89 plugins/consoles/win32cons/src/win32cons.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/consoles/win32cons/src/win32cons.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,605 @@ +// win32cons.cpp +// +// Copyright (c) 2010 Accenture. All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Accenture - Initial contribution +// + +// os_version.h must be included before the windows headers +// to ensure that _WIN32_WINNT has the right value +#include "os_version.h" +#include +#include +#include +#include +#include +#include "win32cons.h" +#include "keymappings.h" + +//#define DEBUG_CONSOLE + +#ifdef DEBUG_CONSOLE +#define CONS_DEBUG(x) x +#else +#define CONS_DEBUG(x) +#endif + +inline int Min(int a, int b) {return a= 0x0501 + if (!::AttachConsole(-1)) + { + // no luck + int err = GetLastError(); + if (err == ERROR_ACCESS_DENIED) + { + WIN32_END(); + // we're already attached to a console (?) - continue. + } + else if (err == ERROR_INVALID_HANDLE) + { +#endif + if (!AllocConsole()) + { + WIN32_END(); + return 0; //failed + } + WIN32_END(); + CONS_DEBUG(DebugMsg("Allocated console")); +#if _WIN32_WINNT >= 0x0501 + } + else + { + WIN32_END(); + CONS_DEBUG(DebugMsg("Failed to attach to console, error %d", err)); + return 0; + } + } + else + { + int err = GetLastError(); + WIN32_END(); + CONS_DEBUG(DebugMsg("Attached console err %d", err)); + } +#endif + + iHaveConsole = true; + return 1; + } + +bool TestHandle(void* aHandle, int* aError = NULL) + { + WIN32_START(); + DWORD written; + bool r = WriteFile(aHandle, "", 0, &written, NULL); + if (aError) *aError = GetLastError(); + WIN32_END(); + return r; + } + +int TWin32Console::AttachConsole() + { + iHaveConsole = false; + // first, test the existing STDOUT and STDERR handles; if out output is being redirected to + // a file, these will be valid as they stand. + WIN32_START(); + iStdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + iStdErrHandle = GetStdHandle(STD_ERROR_HANDLE); + + // try writing to stdout to see if it's valid + int err; + bool ok = TestHandle(iStdOutHandle, &err); + WIN32_END(); + + if (!ok && err == ERROR_INVALID_HANDLE) + { + // no redirection, so existing handle doesn't work. Attach to an existing console (if it exists) + // or create a new one (this will happen if we're invoked from the IDE, for example) + CONS_DEBUG(DebugMsg("Failed to write to existing STDOUT")); + err = AttachOrAllocConsole(); + if (err==0) return err; + WIN32_CALL(iStdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE)); + } + else if (!ok) + { + // some other error occurred, fail. + CONS_DEBUG(DebugMsg("Failed to write to existing STDOUT; error %d", err)); + return 0; + } + else + { + CONS_DEBUG(DebugMsg("Using existing STDOUT")); + } + + // try writing to stderr to see if it's valid + ok = TestHandle(iStdErrHandle, &err); + if (!ok && err == ERROR_INVALID_HANDLE) + { + CONS_DEBUG(DebugMsg("Failed to write to existing STDERR")); + if (!iHaveConsole) + { + err = AttachOrAllocConsole(); + if (err==0) return err; + } + else + { + CONS_DEBUG(DebugMsg("Already attached/alloc'd console")); + } + WIN32_CALL(iStdErrHandle = GetStdHandle(STD_ERROR_HANDLE)); + CONS_DEBUG(DebugMsg("GetStdHandle(Stderr)=%d err=%d", iStdErrHandle, GetLastError())); + } + else if (!ok) + { + CONS_DEBUG(DebugMsg("Failed to write to existing STDERR; error %d", err)); + return 0; + } + else + { + CONS_DEBUG(DebugMsg("Using existing STDERR")); + } + + // in some cases (such as when we're invoked by GNU make) the handle returned by GetStdHandle + // after attaching to a console doesn't work. In this case, open a new handle to the console + // using CreateFile + TInt lastError; + if (!TestHandle(iStdOutHandle, &lastError)) + { + CONS_DEBUG(DebugMsg("Still can't write STDOUT; err %d; opening new console handle", lastError)); + + WIN32_START(); + iHaveConsole = false; + iStdOutHandle = CreateFile(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + int err = GetLastError(); + WIN32_END(); + CONS_DEBUG(DebugMsg("CreateFile handle = %d, err %d", iStdOutHandle, err)); + if (!TestHandle(iStdOutHandle, &err)) + { + CONS_DEBUG(DebugMsg("Still can't write STDOUT; err %d; giving up", err)); + return 0; + } + } + + if (!TestHandle(iStdErrHandle, &lastError)) + { + CONS_DEBUG(DebugMsg("Still can't write STDOUT; err %d; opening new console handle", GetLastError())); + + WIN32_START(); + iHaveConsole = false; + iStdErrHandle = CreateFile(L"CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + int err = GetLastError(); + WIN32_END(); + + CONS_DEBUG(DebugMsg("CreateFile handle = %d, err %d", iStdErrHandle, err)); + if (!TestHandle(iStdErrHandle, &err)) + { + CONS_DEBUG(DebugMsg("Still can't write STDERR; err %d; giving up", err)); + return 0; + } + } + + //iStdinHandle = CreateFile(L"CONIN$", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + WIN32_START(); + iConsModeSet=false; + iStdinHandle = GetStdHandle(STD_INPUT_HANDLE); + if (GetConsoleMode(iStdinHandle, &iOldConsMode)) + { + DWORD consmode = iOldConsMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); + if (SetConsoleMode(iStdinHandle, consmode)) + { + iConsModeSet=true; + } + } + iOrigAttributes = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; + CONSOLE_SCREEN_BUFFER_INFO info; + if (GetConsoleScreenBufferInfo(iStdOutHandle, &info)) + { + iOrigAttributes = info.wAttributes; + } + WIN32_END(); + + return 1; + } + +void TWin32Console::FreeConsole() + { + WIN32_START(); + if (iConsModeSet) + { + SetConsoleMode(iStdinHandle, iOldConsMode); + } + ::FreeConsole(); + WIN32_END(); + } + +int TWin32Console::Write(const char* aText, int aLen) + { + DWORD written; + WIN32_START(); + bool b = WriteFile(iStdOutHandle, (const void*)aText, aLen, &written, NULL); + int err = GetLastError(); + WIN32_END(); + //DebugMsg("WriteFile(STDOUT)=%d, err=%d", b, err); + return b ? 0 : (err>0 ? -err : err); + } + +int TWin32Console::Write(const WCHAR* aText, int aLen) + { + if (iHaveConsole) + { + DWORD written; + WIN32_START(); + bool b = WriteConsole(iStdOutHandle, (void*)aText, aLen, &written, NULL); + int err = GetLastError(); + WIN32_END(); + //DebugMsg("WriteFile(STDOUT)=%d, err=%d", b, err); + return b ? 0 : (err>0 ? -err : err); + } + else + { + const int KBufLen = 256; + char outbuf[KBufLen]; + int pos = 0; + while (pos < aLen) + { + int len = Min(KBufLen, aLen - pos); + int narrowLen = WideCharToMultiByte(CP_ACP, 0, aText, len, outbuf, KBufLen, NULL, NULL); + if (narrowLen == 0 && aLen != 0) return 0; + int ok = Write(outbuf, narrowLen); + if (!ok) return ok; + } + return 1; + } + } + +int TWin32Console::WriteStdErr(const char* aText, int aLen) + { + DWORD written; + WIN32_START(); + bool b = WriteFile(iStdErrHandle, (const void*)aText, aLen, &written, NULL); + int err = GetLastError(); + WIN32_END(); + //DebugMsg("WriteFile(STDERR)=%d, err=%d", b, err); + return b ? 0 : (err>0 ? -err : err); + } + +int TWin32Console::WriteStdErr(const WCHAR* aText, int aLen) + { + if (iHaveConsole) + { + DWORD written; + WIN32_START(); + bool b = WriteConsole(iStdErrHandle, (void*)aText, aLen, &written, NULL); + int err = GetLastError(); + WIN32_END(); + //DebugMsg("WriteFile(STDOUT)=%d, err=%d", b, err); + return b ? 0 : (err>0 ? -err : err); + } + else + { + const int KBufLen = 256; + char outbuf[KBufLen]; + int pos = 0; + while (pos < aLen) + { + int len = Min(KBufLen, aLen - pos); + int narrowLen = WideCharToMultiByte(CP_ACP, 0, aText, len, outbuf, KBufLen, NULL, NULL); + if (narrowLen == 0 && aLen != 0) return 0; + int ok = WriteStdErr(outbuf, narrowLen); + if (!ok) return ok; + } + return 1; + } + } + +int TWin32Console::Read(void* buf, int len, int& bytesRead) + { + DWORD read; + + /* + Read is a blocking call. We don't want to use WIN32_START() (Emulator::Lock()) as this + will cause all other emulator threads to block until the Read call returns. Instead, + we escape the emulator which removes this thread from the usual emulator thraed + scheduling until we re-enter the emulator when ReadFile returns. This allows other + emulator threads to run concurrently, even though (from the emulators thread shedulers + point of view) this thread is still running. + */ + Emulator::Escape(); + bool b = ReadFile(iStdinHandle, buf, len, &read, NULL); + Emulator::Reenter(); + + bytesRead = read; + return b ? 0 : -2; // TODO better error code? + } + +int TWin32Console::ReadKey(TKeyCode& aCode, TUint& aModifiers) + { + bool ok = true; + if (iCachedKey.wRepeatCount == 0) + { + Emulator::Escape(); + // read a recognised key event: + do + { + // read a key down event: + do + { + DWORD read; + INPUT_RECORD inp; + // read a key event: + do + { + // read a console event: + do + { + ok = ReadConsoleInput(iStdinHandle, &inp, 1, &read); + } while (ok && (read==0)); + } while (ok && (inp.EventType != KEY_EVENT)); + + if (ok) + { + iCachedKey = inp.Event.KeyEvent; + } + } while (ok && !iCachedKey.bKeyDown); + aCode = (TKeyCode)iCachedKey.uChar.AsciiChar; + if (aCode==0) aCode = GetSymbianKeyCode(iCachedKey.wVirtualKeyCode); + aModifiers = GetSymbianModifiers(iCachedKey.dwControlKeyState); + + } while (ok && (aCode==0)); + + Emulator::Reenter(); + + } + if (!ok) return -2; + + CONS_DEBUG(char ch = iCachedKey.uChar.AsciiChar; + if (!ch) ch=' '; + DebugMsg("KeyPress %02x ('%c') down:%d keycode:%02x scancode:%02x controlkey:%02x", + iCachedKey.uChar.AsciiChar, ch, iCachedKey.bKeyDown, iCachedKey.wVirtualKeyCode, iCachedKey.wVirtualScanCode, iCachedKey.dwControlKeyState)); + + if (iCachedKey.wRepeatCount>0) --iCachedKey.wRepeatCount; + + return 0; + } + + +int TWin32Console::GetCursorPos(int& aX, int& aY) const + { + CONSOLE_SCREEN_BUFFER_INFO sbInfo; + int r; + WIN32_CALL(r = GetConsoleScreenBufferInfo(iStdOutHandle, &sbInfo)); + if (r) + { + aX = sbInfo.dwCursorPosition.X; + aY = sbInfo.dwCursorPosition.Y; + // make sure it's not off the screen (bottom right) + if (aX > sbInfo.srWindow.Right) aX = sbInfo.srWindow.Left; + if (aY > sbInfo.srWindow.Bottom) aY = sbInfo.srWindow.Bottom; + // make cursor pos relative to display window + aX -= sbInfo.srWindow.Left; + aY -= sbInfo.srWindow.Top; + // make sure it's not off the screen (top left) + if (aX < 0) aX = 0; + if (aY < 0) aY = 0; + } + else + { + aX = aY = 0; + } + return r; + } + +int TWin32Console::SetCursorPos(int aX, int aY) + { + CONSOLE_SCREEN_BUFFER_INFO sbInfo; + int r; + WIN32_CALL(r = GetConsoleScreenBufferInfo(iStdOutHandle, &sbInfo)); + if (r) + { + // make position relative to display window + aX += sbInfo.srWindow.Left; + aY += sbInfo.srWindow.Top; + // make sure it's within the screen buffer + if (aX > sbInfo.dwSize.X) aX = sbInfo.dwSize.X; + if (aY > sbInfo.dwSize.Y) aY = sbInfo.dwSize.Y; + if (aX < 0) aX = 0; + if (aY < 0) aY = 0; + COORD pos; + pos.X = aX; + pos.Y = aY; + WIN32_CALL(r = SetConsoleCursorPosition(iStdOutHandle, pos)); + } + return r; + } + +int TWin32Console::SetCursorPosRel(int aX, int aY) + { + CONSOLE_SCREEN_BUFFER_INFO sbInfo; + int r; + WIN32_CALL(r = GetConsoleScreenBufferInfo(iStdOutHandle, &sbInfo)); + if (r) + { + sbInfo.dwCursorPosition.X += aX; + sbInfo.dwCursorPosition.Y += aY; + // make sure it's still within screen buffer + sbInfo.dwCursorPosition.X = Max(0, Min(sbInfo.dwCursorPosition.X, sbInfo.dwSize.X)); + sbInfo.dwCursorPosition.Y = Max(0, Min(sbInfo.dwCursorPosition.Y, sbInfo.dwSize.Y)); + + WIN32_CALL(r = SetConsoleCursorPosition(iStdOutHandle, sbInfo.dwCursorPosition)); + } + return r; + } + +int TWin32Console::SetCursorHeight(int aPercentage) + { + CONSOLE_CURSOR_INFO info; + info.dwSize = Min(100, Max(1, aPercentage)); + info.bVisible = aPercentage>0; + int r; + WIN32_CALL(r = SetConsoleCursorInfo(iStdOutHandle, &info)); + return r; + } + +int TWin32Console::SetTitle(const char* aTitle) + { + int r; + WIN32_CALL(r = SetConsoleTitleA(aTitle)); + return r; + } + +int TWin32Console::ClearScreen() + { + CONSOLE_SCREEN_BUFFER_INFO sbInfo; + WIN32_START(); + int r = GetConsoleScreenBufferInfo(iStdOutHandle, &sbInfo); + COORD topLeft = {0, 0}; + if (r) + { + DWORD bufferSize = sbInfo.dwSize.X * sbInfo.dwSize.Y; + DWORD charsWritten; + r = FillConsoleOutputCharacter(iStdOutHandle, (TCHAR)' ', bufferSize, topLeft, &charsWritten); + // Also reset the attributes + if (r) FillConsoleOutputAttribute(iStdOutHandle, iOrigAttributes, bufferSize, topLeft, &charsWritten); + } + if (r) + { + // move cursor to top left + r = SetConsoleCursorPosition(iStdOutHandle, topLeft); + } + if (r) + { + // move scroll region to top left + sbInfo.srWindow.Left -= sbInfo.srWindow.Right; + sbInfo.srWindow.Right -= sbInfo.srWindow.Right; + sbInfo.srWindow.Bottom -= sbInfo.srWindow.Top; + sbInfo.srWindow.Top -= sbInfo.srWindow.Top; + r = SetConsoleWindowInfo(iStdOutHandle, true, &sbInfo.srWindow); + } + WIN32_END(); + return r; + } + +int TWin32Console::ClearToEndOfLine() + { + WIN32_START(); + CONSOLE_SCREEN_BUFFER_INFO sbInfo; + int r = GetConsoleScreenBufferInfo(iStdOutHandle, &sbInfo); + if (r) + { + SHORT len = sbInfo.dwSize.X - sbInfo.dwCursorPosition.X; + DWORD charsWritten; + r = FillConsoleOutputCharacter(iStdOutHandle, (TCHAR)' ', len, sbInfo.dwCursorPosition, &charsWritten); + // Also reset the attributes + if (r) FillConsoleOutputAttribute(iStdOutHandle, iOrigAttributes, len, sbInfo.dwCursorPosition, &charsWritten); + } + WIN32_END(); + return r; + } + +int TWin32Console::GetScreenSize(int& aWidth, int& aHeight) const + { + CONSOLE_SCREEN_BUFFER_INFO sbInfo; + int r; + WIN32_CALL(r = GetConsoleScreenBufferInfo(iStdOutHandle, &sbInfo)); + if (r) + { + aWidth = sbInfo.srWindow.Right - sbInfo.srWindow.Left + 1; + aHeight = sbInfo.srWindow.Bottom - sbInfo.srWindow.Top + 1; + } + else + { + // not much else we can do? + aWidth = 80; + aHeight = 25; // [Shouldn't this be 24? -TomS] + } + return r; + } + +int TWin32Console::SetAttributes(unsigned aAttributes, TWin32Console::TColor aForegroundColor, TWin32Console::TColor aBackgroundColor) + { + WORD attrib; + CONSOLE_SCREEN_BUFFER_INFO info; + if (aAttributes & ENone) + { + // None means reset + attrib = iOrigAttributes; + } + else + { + BOOL ok; + WIN32_CALL(ok = GetConsoleScreenBufferInfo(iStdOutHandle, &info)); + if (!ok) return ok; + attrib = info.wAttributes; + } + + if (aForegroundColor != EUnchanged) attrib &= ~0x0F; // Clear the fg bits in preparation for the switch below + if (aBackgroundColor != EUnchanged) attrib &= ~0xF0; // Clear the bg bits in preparation for the switch below + + switch (aForegroundColor) + { + case EBlack: + break; + case ERed: + attrib |= FOREGROUND_RED; break; + case EGreen: + attrib |= FOREGROUND_GREEN; break; + case EYellow: + attrib |= FOREGROUND_RED | FOREGROUND_GREEN; break; + case EBlue: + attrib |= FOREGROUND_BLUE; break; + case EMagenta: + attrib |= FOREGROUND_RED | FOREGROUND_BLUE; break; + case ECyan: + attrib |= FOREGROUND_BLUE | FOREGROUND_GREEN; break; + case EWhite: + attrib |= FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; break; + default: + break; + } + switch (aBackgroundColor) + { + case EBlack: + break; + case ERed: + attrib |= BACKGROUND_RED; break; + case EGreen: + attrib |= BACKGROUND_GREEN; break; + case EYellow: + attrib |= BACKGROUND_RED | BACKGROUND_GREEN; break; + case EBlue: + attrib |= BACKGROUND_BLUE; break; + case EMagenta: + attrib |= BACKGROUND_RED | BACKGROUND_BLUE; break; + case ECyan: + attrib |= BACKGROUND_BLUE | BACKGROUND_GREEN; break; + case EWhite: + attrib |= BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN; break; + default: + break; + } + if (aAttributes & EBold) attrib |= FOREGROUND_INTENSITY; + if (aAttributes & EInverse) + { + // COMMON_LVB_REVERSE_VIDEO doesn't seem to be supported, so swap foreground and background manually + attrib = ((attrib & 0xF) << 4) | ((attrib & 0xF0) >> 4); + } + //if (aAttributes & EUnderscore) attrib |= COMMON_LVB_UNDERSCORE; + + BOOL ok; + WIN32_CALL(ok = SetConsoleTextAttribute(iStdOutHandle, attrib)); + return ok; + }