Alien-uv

 view release on metacpan or  search on metacpan

libuv/src/win/tty.c  view on Meta::CPAN



static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
  uv_read_t* req;
  BOOL r;

  assert(handle->flags & UV_HANDLE_READING);
  assert(!(handle->flags & UV_HANDLE_READ_PENDING));

  assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);

  handle->tty.rd.read_line_buffer = uv_null_buf_;

  req = &handle->read_req;
  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));

  r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait,
                                  handle->handle,
                                  uv_tty_post_raw_read,
                                  (void*) req,
                                  INFINITE,
                                  WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
  if (!r) {
    handle->tty.rd.read_raw_wait = NULL;
    SET_REQ_ERROR(req, GetLastError());
    uv_insert_pending_req(loop, (uv_req_t*)req);
  }

  handle->flags |= UV_HANDLE_READ_PENDING;
  handle->reqs_pending++;
}


static DWORD CALLBACK uv_tty_line_read_thread(void* data) {
  uv_loop_t* loop;
  uv_tty_t* handle;
  uv_req_t* req;
  DWORD bytes, read_bytes;
  WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3];
  DWORD chars, read_chars;
  LONG status;
  COORD pos;
  BOOL read_console_success;

  assert(data);

  req = (uv_req_t*) data;
  handle = (uv_tty_t*) req->data;
  loop = handle->loop;

  assert(handle->tty.rd.read_line_buffer.base != NULL);
  assert(handle->tty.rd.read_line_buffer.len > 0);

  /* ReadConsole can't handle big buffers. */
  if (handle->tty.rd.read_line_buffer.len < MAX_INPUT_BUFFER_LENGTH) {
    bytes = handle->tty.rd.read_line_buffer.len;
  } else {
    bytes = MAX_INPUT_BUFFER_LENGTH;
  }

  /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8
   * codeunits to encode. */
  chars = bytes / 3;

  status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS);
  if (status == TRAP_REQUESTED) {
    SET_REQ_SUCCESS(req);
    req->u.io.overlapped.InternalHigh = 0;
    POST_COMPLETION_FOR_REQ(loop, req);
    return 0;
  }

  read_console_success = ReadConsoleW(handle->handle,
                                      (void*) utf16,
                                      chars,
                                      &read_chars,
                                      NULL);

  if (read_console_success) {
    read_bytes = WideCharToMultiByte(CP_UTF8,
                                     0,
                                     utf16,
                                     read_chars,
                                     handle->tty.rd.read_line_buffer.base,
                                     bytes,
                                     NULL,
                                     NULL);
    SET_REQ_SUCCESS(req);
    req->u.io.overlapped.InternalHigh = read_bytes;
  } else {
    SET_REQ_ERROR(req, GetLastError());
  }

  status = InterlockedExchange(&uv__read_console_status, COMPLETED);

  if (status ==  TRAP_REQUESTED) {
    /* If we canceled the read by sending a VK_RETURN event, restore the
       screen state to undo the visual effect of the VK_RETURN */
    if (read_console_success && InterlockedOr(&uv__restore_screen_state, 0)) {
      HANDLE active_screen_buffer;
      active_screen_buffer = CreateFileA("conout$",
                                         GENERIC_READ | GENERIC_WRITE,
                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                                         NULL,
                                         OPEN_EXISTING,
                                         FILE_ATTRIBUTE_NORMAL,
                                         NULL);
      if (active_screen_buffer != INVALID_HANDLE_VALUE) {
        pos = uv__saved_screen_state.dwCursorPosition;

        /* If the cursor was at the bottom line of the screen buffer, the
           VK_RETURN would have caused the buffer contents to scroll up by one
           line. The right position to reset the cursor to is therefore one line
           higher */
        if (pos.Y == uv__saved_screen_state.dwSize.Y - 1)
          pos.Y--;

        SetConsoleCursorPosition(active_screen_buffer, pos);
        CloseHandle(active_screen_buffer);
      }
    }

libuv/src/win/tty.c  view on Meta::CPAN

  off_t buf_used;

  assert(handle->type == UV_TTY);
  assert(handle->flags & UV_HANDLE_TTY_READABLE);
  handle->flags &= ~UV_HANDLE_READ_PENDING;

  if (!(handle->flags & UV_HANDLE_READING) ||
      !(handle->flags & UV_HANDLE_TTY_RAW)) {
    goto out;
  }

  if (!REQ_SUCCESS(req)) {
    /* An error occurred while waiting for the event. */
    if ((handle->flags & UV_HANDLE_READING)) {
      handle->flags &= ~UV_HANDLE_READING;
      handle->read_cb((uv_stream_t*)handle,
                      uv_translate_sys_error(GET_REQ_ERROR(req)),
                      &uv_null_buf_);
    }
    goto out;
  }

  /* Fetch the number of events  */
  if (!GetNumberOfConsoleInputEvents(handle->handle, &records_left)) {
    handle->flags &= ~UV_HANDLE_READING;
    DECREASE_ACTIVE_COUNT(loop, handle);
    handle->read_cb((uv_stream_t*)handle,
                    uv_translate_sys_error(GetLastError()),
                    &uv_null_buf_);
    goto out;
  }

  /* Windows sends a lot of events that we're not interested in, so buf will be
   * allocated on demand, when there's actually something to emit. */
  buf = uv_null_buf_;
  buf_used = 0;

  while ((records_left > 0 || handle->tty.rd.last_key_len > 0) &&
         (handle->flags & UV_HANDLE_READING)) {
    if (handle->tty.rd.last_key_len == 0) {
      /* Read the next input record */
      if (!ReadConsoleInputW(handle->handle,
                             &handle->tty.rd.last_input_record,
                             1,
                             &records_read)) {
        handle->flags &= ~UV_HANDLE_READING;
        DECREASE_ACTIVE_COUNT(loop, handle);
        handle->read_cb((uv_stream_t*) handle,
                        uv_translate_sys_error(GetLastError()),
                        &buf);
        goto out;
      }
      records_left--;

      /* Ignore other events that are not key events. */
      if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) {
        continue;
      }

      /* Ignore keyup events, unless the left alt key was held and a valid
       * unicode character was emitted. */
      if (!KEV.bKeyDown &&
          (KEV.wVirtualKeyCode != VK_MENU ||
           KEV.uChar.UnicodeChar == 0)) {
        continue;
      }

      /* Ignore keypresses to numpad number keys if the left alt is held
       * because the user is composing a character, or windows simulating this.
       */
      if ((KEV.dwControlKeyState & LEFT_ALT_PRESSED) &&
          !(KEV.dwControlKeyState & ENHANCED_KEY) &&
          (KEV.wVirtualKeyCode == VK_INSERT ||
          KEV.wVirtualKeyCode == VK_END ||
          KEV.wVirtualKeyCode == VK_DOWN ||
          KEV.wVirtualKeyCode == VK_NEXT ||
          KEV.wVirtualKeyCode == VK_LEFT ||
          KEV.wVirtualKeyCode == VK_CLEAR ||
          KEV.wVirtualKeyCode == VK_RIGHT ||
          KEV.wVirtualKeyCode == VK_HOME ||
          KEV.wVirtualKeyCode == VK_UP ||
          KEV.wVirtualKeyCode == VK_PRIOR ||
          KEV.wVirtualKeyCode == VK_NUMPAD0 ||
          KEV.wVirtualKeyCode == VK_NUMPAD1 ||
          KEV.wVirtualKeyCode == VK_NUMPAD2 ||
          KEV.wVirtualKeyCode == VK_NUMPAD3 ||
          KEV.wVirtualKeyCode == VK_NUMPAD4 ||
          KEV.wVirtualKeyCode == VK_NUMPAD5 ||
          KEV.wVirtualKeyCode == VK_NUMPAD6 ||
          KEV.wVirtualKeyCode == VK_NUMPAD7 ||
          KEV.wVirtualKeyCode == VK_NUMPAD8 ||
          KEV.wVirtualKeyCode == VK_NUMPAD9)) {
        continue;
      }

      if (KEV.uChar.UnicodeChar != 0) {
        int prefix_len, char_len;

        /* Character key pressed */
        if (KEV.uChar.UnicodeChar >= 0xD800 &&
            KEV.uChar.UnicodeChar < 0xDC00) {
          /* UTF-16 high surrogate */
          handle->tty.rd.last_utf16_high_surrogate = KEV.uChar.UnicodeChar;
          continue;
        }

        /* Prefix with \u033 if alt was held, but alt was not used as part a
         * compose sequence. */
        if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
            && !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED |
            RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) {
          handle->tty.rd.last_key[0] = '\033';
          prefix_len = 1;
        } else {
          prefix_len = 0;
        }

        if (KEV.uChar.UnicodeChar >= 0xDC00 &&
            KEV.uChar.UnicodeChar < 0xE000) {
          /* UTF-16 surrogate pair */
          WCHAR utf16_buffer[2];

libuv/src/win/tty.c  view on Meta::CPAN

        }

        continue;
      }

      /* Apply dwRepeat from the last input record. */
      if (--KEV.wRepeatCount > 0) {
        handle->tty.rd.last_key_offset = 0;
        continue;
      }

      handle->tty.rd.last_key_len = 0;
      continue;
    }
  }

  /* Send the buffer back to the user */
  if (buf_used > 0) {
    handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
  }

 out:
  /* Wait for more input events. */
  if ((handle->flags & UV_HANDLE_READING) &&
      !(handle->flags & UV_HANDLE_READ_PENDING)) {
    uv_tty_queue_read(loop, handle);
  }

  DECREASE_PENDING_REQ_COUNT(handle);

#undef KEV
}



void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
    uv_req_t* req) {
  uv_buf_t buf;

  assert(handle->type == UV_TTY);
  assert(handle->flags & UV_HANDLE_TTY_READABLE);

  buf = handle->tty.rd.read_line_buffer;

  handle->flags &= ~UV_HANDLE_READ_PENDING;
  handle->tty.rd.read_line_buffer = uv_null_buf_;

  if (!REQ_SUCCESS(req)) {
    /* Read was not successful */
    if (handle->flags & UV_HANDLE_READING) {
      /* Real error */
      handle->flags &= ~UV_HANDLE_READING;
      DECREASE_ACTIVE_COUNT(loop, handle);
      handle->read_cb((uv_stream_t*) handle,
                      uv_translate_sys_error(GET_REQ_ERROR(req)),
                      &buf);
    }
  } else {
    if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING) &&
        req->u.io.overlapped.InternalHigh != 0) {
      /* Read successful. TODO: read unicode, convert to utf-8 */
      DWORD bytes = req->u.io.overlapped.InternalHigh;
      handle->read_cb((uv_stream_t*) handle, bytes, &buf);
    }
    handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING;
  }

  /* Wait for more input events. */
  if ((handle->flags & UV_HANDLE_READING) &&
      !(handle->flags & UV_HANDLE_READ_PENDING)) {
    uv_tty_queue_read(loop, handle);
  }

  DECREASE_PENDING_REQ_COUNT(handle);
}


void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
    uv_req_t* req) {
  assert(handle->type == UV_TTY);
  assert(handle->flags & UV_HANDLE_TTY_READABLE);

  /* If the read_line_buffer member is zero, it must have been an raw read.
   * Otherwise it was a line-buffered read. FIXME: This is quite obscure. Use a
   * flag or something. */
  if (handle->tty.rd.read_line_buffer.len == 0) {
    uv_process_tty_read_raw_req(loop, handle, req);
  } else {
    uv_process_tty_read_line_req(loop, handle, req);
  }
}


int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
    uv_read_cb read_cb) {
  uv_loop_t* loop = handle->loop;

  if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
    return ERROR_INVALID_PARAMETER;
  }

  handle->flags |= UV_HANDLE_READING;
  INCREASE_ACTIVE_COUNT(loop, handle);
  handle->read_cb = read_cb;
  handle->alloc_cb = alloc_cb;

  /* If reading was stopped and then started again, there could still be a read
   * request pending. */
  if (handle->flags & UV_HANDLE_READ_PENDING) {
    return 0;
  }

  /* Maybe the user stopped reading half-way while processing key events.
   * Short-circuit if this could be the case. */
  if (handle->tty.rd.last_key_len > 0) {
    SET_REQ_SUCCESS(&handle->read_req);
    uv_insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req);
    /* Make sure no attempt is made to insert it again until it's handled. */
    handle->flags |= UV_HANDLE_READ_PENDING;
    handle->reqs_pending++;
    return 0;



( run in 0.622 second using v1.01-cache-2.11-cpan-119454b85a5 )