--- /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 <stdio.h>
+#include <windows.h>
+#include <fcntl.h>
+#include <io.h>
+#include <emulator.h>
+#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<b ? a : b;}
+inline int Max(int a, int b) {return a<b ? b : a;}
+
+#define WIN32_START() Emulator::Lock()
+#define WIN32_END() Emulator::Unlock()
+
+#define WIN32_CALL(x) { WIN32_START(); x; WIN32_END(); }
+
+int TWin32Console::AttachOrAllocConsole()
+ {
+ // NB The AttachConsole method is only available on WinXP and later
+ WIN32_START();
+#if _WIN32_WINNT >= 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;
+ }