diff options
| author | Petr Mrázek | 2011-07-14 04:05:27 +0200 |
|---|---|---|
| committer | Petr Mrázek | 2011-07-14 04:05:27 +0200 |
| commit | 792d272fbf3a1a80d597757300b9a33ea083bcc2 (patch) | |
| tree | 9db1134457cb6a980ab8081e64bacd61aebafdcd /library/Console-windows.cpp | |
| parent | 630b746cfe8455ee89ef8af55013141aa70ccf87 (diff) | |
| download | dfhack-792d272fbf3a1a80d597757300b9a33ea083bcc2.tar.gz dfhack-792d272fbf3a1a80d597757300b9a33ea083bcc2.tar.bz2 dfhack-792d272fbf3a1a80d597757300b9a33ea083bcc2.tar.xz | |
Windows side of the Console rewrite
Diffstat (limited to 'library/Console-windows.cpp')
| -rw-r--r-- | library/Console-windows.cpp | 280 |
1 files changed, 258 insertions, 22 deletions
diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index 3655b66d..8c148093 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -25,6 +25,7 @@ distribution. #include <conio.h> #include <stdarg.h> +#include "dfhack/extra/stdiostream.h" #include < process.h> #include <errno.h> #include <stdio.h> @@ -39,21 +40,57 @@ distribution. #include <cstdio> #include <cstdlib> #include <sstream> +#include <deque> using namespace DFHack; // FIXME: maybe make configurable with an ini option? #define MAX_CONSOLE_LINES 999; -duthomhas::stdiostream dfout; -FILE * dfout_C = 0; -duthomhas::stdiobuf * stream_o = 0; +namespace DFHack +{ + class Private + { + public: + Private() + { + dfout_C = 0; + stream_o = 0; + rawmode = 0; + console_in = 0; + console_out = 0; + ConsoleWindow = 0; + default_attributes = 0; + }; + void output(const char* str, size_t len, int x, int y) + { + COORD pos = { (SHORT)x, (SHORT)y }; + DWORD count = 0; + WriteConsoleOutputCharacterA(console_out, str, len, pos, &count); + } + + FILE * dfout_C; + duthomhas::stdiobuf * stream_o; + int rawmode; /* for atexit() function to check if restore is needed*/ + std::deque <std::string> history; + + HANDLE console_in; + HANDLE console_out; + HWND ConsoleWindow; + WORD default_attributes; + }; +} + + +Console::Console():std::ostream(0), std::ios(0) +{ + d = new Private(); +} -HANDLE g_hConsoleOut; // Handle to debug console -HWND ConsoleWindow; -WORD default_attributes; +Console::~Console() +{ +} -// FIXME: prime candidate for being a singleton... indeed. -Console::Console() +bool Console::init(void) { int hConHandle; long lStdHandle; @@ -63,25 +100,26 @@ Console::Console() // Allocate a console! AllocConsole(); - ConsoleWindow = GetConsoleWindow(); - HMENU hm = GetSystemMenu(ConsoleWindow,false); + d->ConsoleWindow = GetConsoleWindow(); + HMENU hm = GetSystemMenu(d->ConsoleWindow,false); DeleteMenu(hm, SC_CLOSE, MF_BYCOMMAND); // set the screen buffer to be big enough to let us scroll text GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - default_attributes = coninfo.wAttributes; + d->default_attributes = coninfo.wAttributes; coninfo.dwSize.Y = MAX_CONSOLE_LINES; // How many lines do you want to have in the console buffer SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); // redirect unbuffered STDOUT to the console - g_hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); - lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); + d->console_out = GetStdHandle(STD_OUTPUT_HANDLE); + lStdHandle = (long)d->console_out; hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - dfout_C = _fdopen( hConHandle, "w" ); - setvbuf( dfout_C, NULL, _IONBF, 0 ); + d->dfout_C = _fdopen( hConHandle, "w" ); + setvbuf( d->dfout_C, NULL, _IONBF, 0 ); // redirect unbuffered STDIN to the console - lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); + d->console_in = GetStdHandle(STD_INPUT_HANDLE); + lStdHandle = (long)d->console_in; hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); fp = _fdopen( hConHandle, "r" ); *stdin = *fp; @@ -92,16 +130,40 @@ Console::Console() std::ios::sync_with_stdio(); // make our own weird streams so our IO isn't redirected - stream_o = new duthomhas::stdiobuf(dfout_C); - dfout.rdbuf(stream_o); - std::cin.tie(&dfout); + d->stream_o = new duthomhas::stdiobuf(d->dfout_C); + rdbuf(d->stream_o); + std::cin.tie(this); clear(); - // result is a terminal controlled by the parasitic code! + return true; } -Console::~Console() +bool Console::shutdown(void) { FreeConsole(); + return true; +} + +int Console::print( const char* format, ... ) +{ + va_list args; + va_start( args, format ); + int ret = vfprintf( d->dfout_C, format, args ); + va_end( args ); + return ret; +} + +int Console::get_columns(void) +{ + CONSOLE_SCREEN_BUFFER_INFO inf = { 0 }; + GetConsoleScreenBufferInfo(d->console_out, &inf); + return (size_t)inf.dwSize.X; +} + +int Console::get_rows(void) +{ + CONSOLE_SCREEN_BUFFER_INFO inf = { 0 }; + GetConsoleScreenBufferInfo(d->console_out, &inf); + return (size_t)inf.dwSize.Y; } void Console::clear() @@ -124,7 +186,7 @@ void Console::color(int index) void Console::reset_color( void ) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(hConsole, default_attributes); + SetConsoleTextAttribute(hConsole, d->default_attributes); } @@ -153,4 +215,178 @@ void Console::cursor(bool enable) void Console::msleep (unsigned int msec) { Sleep(msec); +} + +int Console::enable_raw() +{ + return 0; +} + +void Console::disable_raw() +{ + /* Nothing to do yet */ +} + +void Console::prompt_refresh( const std::string& prompt, const std::string& buffer, size_t pos) +{ + size_t cols = get_columns(); + size_t plen = prompt.size(); + const char * buf = buffer.c_str(); + size_t len = buffer.size(); + + while ((plen + pos) >= cols) + { + buf++; + len--; + pos--; + } + while (plen + len > cols) + { + len--; + } + + CONSOLE_SCREEN_BUFFER_INFO inf = { 0 }; + GetConsoleScreenBufferInfo(d->console_out, &inf); + d->output(prompt.c_str(), plen, 0, inf.dwCursorPosition.Y); + d->output(buf, len, plen, inf.dwCursorPosition.Y); + if (plen + len < (size_t)inf.dwSize.X) + { + /* Blank to EOL */ + char* tmp = (char*)malloc(inf.dwSize.X - (plen + len)); + memset(tmp, ' ', inf.dwSize.X - (plen + len)); + d->output(tmp, inf.dwSize.X - (plen + len), len + plen, inf.dwCursorPosition.Y); + free(tmp); + } + inf.dwCursorPosition.X = (SHORT)(pos + plen); + SetConsoleCursorPosition(d->console_out, inf.dwCursorPosition); +} + +int Console::prompt_loop(const std::string & prompt, std::string & buffer) +{ + buffer.clear(); // make sure the buffer is empty! + size_t plen = prompt.size(); + size_t pos = 0; + int history_index = 0; + + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + history_add(""); + + CONSOLE_SCREEN_BUFFER_INFO inf = { 0 }; + GetConsoleScreenBufferInfo(d->console_out, &inf); + size_t cols = inf.dwSize.X; + d->output(prompt.c_str(), plen, 0, inf.dwCursorPosition.Y); + inf.dwCursorPosition.X = (SHORT)plen; + SetConsoleCursorPosition(d->console_out, inf.dwCursorPosition); + + while (1) + { + INPUT_RECORD rec; + DWORD count; + ReadConsoleInputA(d->console_in, &rec, 1, &count); + if (rec.EventType != KEY_EVENT || !rec.Event.KeyEvent.bKeyDown) + continue; + switch (rec.Event.KeyEvent.wVirtualKeyCode) + { + case VK_RETURN: // enter + d->history.pop_front(); + return buffer.size(); + case VK_BACK: // backspace + if (pos > 0 && buffer.size() > 0) + { + buffer.erase(pos-1,1); + pos--; + prompt_refresh(prompt,buffer,pos); + } + break; + case VK_LEFT: // left arrow + if (pos > 0) + { + pos--; + prompt_refresh(prompt,buffer,pos); + } + break; + case VK_RIGHT: // right arrow + if (pos != buffer.size()) + { + pos++; + prompt_refresh(prompt,buffer,pos); + } + break; + case VK_UP: + case VK_DOWN: + /* up and down arrow: history */ + if (d->history.size() > 1) + { + /* Update the current history entry before to + * overwrite it with tne next one. */ + d->history[history_index] = buffer; + /* Show the new entry */ + history_index += (rec.Event.KeyEvent.wVirtualKeyCode == VK_UP) ? 1 : -1; + if (history_index < 0) + { + history_index = 0; + break; + } + else if (history_index >= d->history.size()) + { + history_index = d->history.size()-1; + break; + } + buffer = d->history[history_index]; + pos = buffer.size(); + prompt_refresh(prompt,buffer,pos); + } + break; + case VK_DELETE: + // delete + if (buffer.size() > 0 && pos < buffer.size()) + { + buffer.erase(pos,1); + prompt_refresh(prompt,buffer,pos); + } + break; + case VK_HOME: + pos = 0; + prompt_refresh(prompt,buffer,pos); + break; + case VK_END: + pos = buffer.size(); + prompt_refresh(prompt,buffer,pos); + break; + default: + if (rec.Event.KeyEvent.uChar.AsciiChar < ' ' || + rec.Event.KeyEvent.uChar.AsciiChar > '~') + continue; + if (buffer.size() == pos) + buffer.append(1,rec.Event.KeyEvent.uChar.AsciiChar); + else + buffer.insert(pos,1,rec.Event.KeyEvent.uChar.AsciiChar); + pos++; + prompt_refresh(prompt,buffer,pos); + break; + } + } +} + +// push to front, remove from back if we are above maximum. ignore immediate duplicates +void Console::history_add(const std::string & command) +{ + if(d->history.front() == command) + return; + d->history.push_front(command); + if(d->history.size() > 100) + d->history.pop_back(); +} + +int Console::lineedit(const std::string & prompt, std::string & output) +{ + output.clear(); + int count; + if (enable_raw() == -1) + return -1; + count = prompt_loop(prompt,output); + disable_raw(); + *this << std::endl; + return count; }
\ No newline at end of file |
