diff --git a/src/node_stdio_win32.cc b/src/node_stdio_win32.cc index 7e49389637..30b572e51c 100644 --- a/src/node_stdio_win32.cc +++ b/src/node_stdio_win32.cc @@ -221,6 +221,40 @@ static Handle IsStdoutBlocking(const Arguments& args) { } +static Handle WriteTTY(const Arguments& args) { + HandleScope scope; + int fd, len; + DWORD written; + HANDLE handle; + + if (!args[0]->IsNumber()) + THROW_BAD_ARGS + + fd = args[0]->IntegerValue(); + handle = (HANDLE)_get_osfhandle(fd); + + Handle data = args[1]->ToString(); + String::Value buf(data); + len = data->Length(); + + if (!WriteConsoleW(handle, reinterpret_cast(*buf), len, &written, NULL)) + return ThrowException(ErrnoException(GetLastError(), "WriteConsole")); + + return scope.Close(Integer::New(written)); +} + + +static Handle CloseTTY(const Arguments& args) { + HandleScope scope; + + int fd = args[0]->IntegerValue(); + if (close(fd) < 0) + return ThrowException(ErrnoException(errno, "close")); + + return Undefined(); +} + + // process.binding('stdio').getWindowSize(fd); // returns [row, col] static Handle GetWindowSize (const Arguments& args) { @@ -245,6 +279,120 @@ static Handle GetWindowSize (const Arguments& args) { } +/* moveCursor(fd, dx, dy) */ +/* cursorTo(fd, x, y) */ +template +static Handle SetCursor(const Arguments& args) { + HandleScope scope; + int fd; + COORD size, pos; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + + if (!args[0]->IsNumber()) + THROW_BAD_ARGS + fd = args[0]->IntegerValue(); + handle = (HANDLE)_get_osfhandle(fd); + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return ThrowException(ErrnoException(GetLastError(), "GetConsoleScreenBufferInfo")); + + pos = info.dwCursorPosition; + if (relative) { + if (args[1]->IsNumber()) + pos.X += static_cast(args[1]->Int32Value()); + if (args[2]->IsNumber()) + pos.Y += static_cast(args[2]->Int32Value()); + } else { + if (args[1]->IsNumber()) + pos.X = static_cast(args[1]->Int32Value()); + if (args[2]->IsNumber()) + pos.Y = static_cast(args[2]->Int32Value()); + } + + size = info.dwSize; + if (pos.X >= size.X) pos.X = size.X - 1; + if (pos.X < 0) pos.X = 0; + if (pos.Y >= size.Y) pos.Y = size.Y - 1; + if (pos.Y < 0) pos.Y = 0; + + if (!SetConsoleCursorPosition(handle, pos)) + return ThrowException(ErrnoException(GetLastError(), "SetConsoleCursorPosition")); + + return Undefined(); +} + + +/* + * ClearLine(fd, direction) + * direction: + * -1: from cursor leftward + * 0: entire line + * 1: from cursor to right + */ +static Handle ClearLine(const Arguments& args) { + HandleScope scope; + int fd, dir; + short x1, x2, count; + WCHAR *buf; + COORD pos; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + DWORD res, written, mode, oldmode; + + if (!args[0]->IsNumber()) + THROW_BAD_ARGS + fd = args[0]->IntegerValue(); + handle = (HANDLE)_get_osfhandle(fd); + + if (args[1]->IsNumber()) + dir = args[1]->IntegerValue(); + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return ThrowException(ErrnoException(GetLastError(), "GetConsoleScreenBufferInfo")); + + x1 = dir <= 0 ? 0 : info.dwCursorPosition.X; + x2 = dir >= 0 ? info.dwSize.X - 1: info.dwCursorPosition.X; + count = x2 - x1 + 1; + + if (x1 != info.dwCursorPosition.X) { + pos.Y = info.dwCursorPosition.Y; + pos.X = x1; + if (!SetConsoleCursorPosition(handle, pos)) + return ThrowException(ErrnoException(GetLastError(), "SetConsoleCursorPosition")); + } + + if (!GetConsoleMode(handle, &oldmode)) + return ThrowException(ErrnoException(GetLastError(), "GetConsoleMode")); + + // Disable wrapping at eol because otherwise windows scrolls the console + // when clearing the last line of the console + mode = oldmode & ~ENABLE_WRAP_AT_EOL_OUTPUT; + if (!SetConsoleMode(handle, mode)) + return ThrowException(ErrnoException(GetLastError(), "SetConsoleMode")); + + buf = new WCHAR[count]; + for (short i = 0; i < count; i++) { + buf[i] = L' '; + } + + res = WriteConsoleW(handle, buf, count, &written, NULL); + + delete[] buf; + + if (!res) + return ThrowException(ErrnoException(GetLastError(), "WriteConsole")); + + if (!SetConsoleCursorPosition(handle, info.dwCursorPosition)) + return ThrowException(ErrnoException(GetLastError(), "SetConsoleCursorPosition")); + + if (!SetConsoleMode(handle, oldmode)) + return ThrowException(ErrnoException(GetLastError(), "SetConsoleMode")); + + return Undefined(); +} + + /* TTY watcher data */ bool tty_watcher_initialized = false; HANDLE tty_handle; @@ -503,6 +651,11 @@ void Stdio::Initialize(v8::Handle target) { NODE_SET_METHOD(target, "isStdinBlocking", IsStdinBlocking); NODE_SET_METHOD(target, "setRawMode", SetRawMode); NODE_SET_METHOD(target, "openStdin", OpenStdin); + NODE_SET_METHOD(target, "writeTTY", WriteTTY); + NODE_SET_METHOD(target, "closeTTY", CloseTTY); + NODE_SET_METHOD(target, "moveCursor", SetCursor); + NODE_SET_METHOD(target, "cursorTo", SetCursor); + NODE_SET_METHOD(target, "clearLine", ClearLine); NODE_SET_METHOD(target, "getWindowSize", GetWindowSize); NODE_SET_METHOD(target, "initTTYWatcher", InitTTYWatcher); NODE_SET_METHOD(target, "destroyTTYWatcher", DestroyTTYWatcher);