New stdio functions to aid new readline interface
authorBert Belder <bertbelder@gmail.com>
Mon, 17 Jan 2011 22:27:14 +0000 (23:27 +0100)
committerRyan Dahl <ry@tinyclouds.org>
Wed, 19 Jan 2011 07:22:38 +0000 (23:22 -0800)
src/node_stdio_win32.cc

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