diff --git a/lib/readline.js b/lib/readline.js index e5feec238c..c85d2410ad 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -49,8 +49,7 @@ function Interface(output, completer) { this.history = []; this.historyIndex = -1; - // 0 for stdin - var winSize = tty.getWindowSize(0); + var winSize = tty.getWindowSize(output.fd); exports.columns = winSize[1]; if (process.listeners('SIGWINCH').length === 0) { diff --git a/src/node_stdio_win32.cc b/src/node_stdio_win32.cc index c609fddd2d..74aafc8a85 100644 --- a/src/node_stdio_win32.cc +++ b/src/node_stdio_win32.cc @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -5,24 +7,23 @@ using namespace v8; namespace node { +#define THROW_ERROR(msg) \ + return ThrowException(Exception::Error(String::New(msg))); +#define THROW_BAD_ARGS \ + return ThrowException(Exception::TypeError(String::New("Bad argument"))); -NO_IMPL(void, Stdio::DisableRawMode, , int fd); -NO_IMPL(void, Stdio::Flush, , ); -NO_IMPL(static Handle, OpenStdin, RET_V8UNDEFINED, const Arguments& args); -NO_IMPL(static Handle, IsStdinBlocking, RET_V8FALSE, const Arguments& args); -NO_IMPL(static Handle, SetRawMode, RET_V8TRUE, const Arguments& args); -NO_IMPL(static Handle, GetColumns, RET_V8INT(80), const Arguments& args); -NO_IMPL(static Handle, GetRows, RET_V8INT(25), const Arguments& args); -NO_IMPL(static Handle, IsATTY, RET_V8FALSE, const Arguments& args); +/* + * Flush stdout and stderr on node exit + * Not necessary on windows, so a no-op + */ +void Stdio::Flush() { +} /* - * STDERR should always be blocking & utf-8 - * TODO: check correctness + * STDERR should always be blocking */ -static Handle -WriteError (const Arguments& args) -{ +static Handle WriteError(const Arguments& args) { HandleScope scope; if (args.Length() < 1) @@ -30,39 +31,128 @@ WriteError (const Arguments& args) String::Utf8Value msg(args[0]->ToString()); - fprintf(stderr, "%s", (char*)*msg); + fprintf(stderr, "%s", reinterpret_cast(*msg)); return Undefined(); } -/* - * Assume that stdout is never blocking on windows - * TODO: check correctness and really implement this - */ -static Handle -IsStdoutBlocking (const Arguments& args) -{ +static Handle IsATTY(const Arguments& args) { + HandleScope scope; + int fd = args[0]->IntegerValue(); + DWORD result; + int r = GetConsoleMode((HANDLE)_get_osfhandle(fd), &result); + return scope.Close(r ? True() : False()); +} + + +/* Whether stdio is currently in raw mode */ +/* -1 means that it has not been set */ +static int rawMode = -1; + + +static void setRawMode(int newMode) { + DWORD flags; + BOOL result; + + if (newMode != rawMode) { + if (newMode) { + // raw input + flags = ENABLE_WINDOW_INPUT; + } else { + // input not raw, but still processing enough messages to make the + // tty watcher work (this mode is not the windows default) + flags = ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | + ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT; + } + + result = SetConsoleMode((HANDLE)_get_osfhandle(STDIN_FILENO), flags); + if (result) { + rawMode = newMode; + } + } +} + + +static Handle SetRawMode(const Arguments& args) { + HandleScope scope; + + int newMode = !args[0]->IsFalse(); + setRawMode(newMode); + + if (newMode != rawMode) { + return ThrowException(ErrnoException(GetLastError(), "EnableRawMode")); + } + + return scope.Close(rawMode ? True() : False()); +} + + + +void Stdio::DisableRawMode(int fd) { + if (rawMode == 1) + setRawMode(0); +} + + +static Handle OpenStdin(const Arguments& args) { + HandleScope scope; + setRawMode(0); // init into nonraw mode + return scope.Close(Integer::New(STDIN_FILENO)); +} + + +static Handle IsStdinBlocking(const Arguments& args) { + // On windows stdin always blocks + return True(); +} + + +static Handle IsStdoutBlocking(const Arguments& args) { + // On windows stdout always blocks return True(); } +// process.binding('stdio').getWindowSize(fd); +// returns [row, col] +static Handle GetWindowSize (const Arguments& args) { + HandleScope scope; + int fd; + 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")); + + Local ret = Array::New(2); + ret->Set(0, Integer::New(static_cast(info.dwSize.Y))); + ret->Set(1, Integer::New(static_cast(info.dwSize.X))); + + return scope.Close(ret); +} + + void Stdio::Initialize(v8::Handle target) { target->Set(String::NewSymbol("stdoutFD"), Integer::New(STDOUT_FILENO)); target->Set(String::NewSymbol("stderrFD"), Integer::New(STDERR_FILENO)); target->Set(String::NewSymbol("stdinFD"), Integer::New(STDIN_FILENO)); NODE_SET_METHOD(target, "writeError", WriteError); - NODE_SET_METHOD(target, "openStdin", OpenStdin); + NODE_SET_METHOD(target, "isatty", IsATTY); NODE_SET_METHOD(target, "isStdoutBlocking", IsStdoutBlocking); NODE_SET_METHOD(target, "isStdinBlocking", IsStdinBlocking); NODE_SET_METHOD(target, "setRawMode", SetRawMode); - NODE_SET_METHOD(target, "getColumns", GetColumns); - NODE_SET_METHOD(target, "getRows", GetRows); - NODE_SET_METHOD(target, "isatty", IsATTY); + NODE_SET_METHOD(target, "openStdin", OpenStdin); + NODE_SET_METHOD(target, "getWindowSize", GetWindowSize); } -} // namespace node +} // namespace node NODE_MODULE(node_stdio, node::Stdio::Initialize);