neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

input.c (16980B)


      1 #include <assert.h>
      2 #include <limits.h>
      3 #include <stdbool.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <uv.h>
      8 
      9 #include "nvim/api/private/defs.h"
     10 #include "nvim/ascii_defs.h"
     11 #include "nvim/autocmd.h"
     12 #include "nvim/autocmd_defs.h"
     13 #include "nvim/buffer_defs.h"
     14 #include "nvim/event/loop.h"
     15 #include "nvim/event/multiqueue.h"
     16 #include "nvim/event/rstream.h"
     17 #include "nvim/getchar.h"
     18 #include "nvim/gettext_defs.h"
     19 #include "nvim/globals.h"
     20 #include "nvim/keycodes.h"
     21 #include "nvim/log.h"
     22 #include "nvim/macros_defs.h"
     23 #include "nvim/main.h"
     24 #include "nvim/msgpack_rpc/channel.h"
     25 #include "nvim/option_vars.h"
     26 #include "nvim/os/input.h"
     27 #include "nvim/os/os_defs.h"
     28 #include "nvim/os/time.h"
     29 #include "nvim/profile.h"
     30 #include "nvim/state.h"
     31 #include "nvim/state_defs.h"
     32 #include "nvim/types_defs.h"
     33 
     34 #define READ_BUFFER_SIZE 0xfff
     35 #define INPUT_BUFFER_SIZE ((READ_BUFFER_SIZE * 4) + MAX_KEY_CODE_LEN)
     36 
     37 static RStream read_stream = { .s.closed = true };  // Input before UI starts.
     38 static char input_buffer[INPUT_BUFFER_SIZE];
     39 static char *input_read_pos = input_buffer;
     40 static char *input_write_pos = input_buffer;
     41 
     42 static bool input_eof = false;
     43 static bool blocking = false;
     44 static int cursorhold_time = 0;  ///< time waiting for CursorHold event
     45 static int cursorhold_tb_change_cnt = 0;  ///< tb_change_cnt when waiting started
     46 
     47 #include "os/input.c.generated.h"
     48 
     49 void input_start(void)
     50 {
     51  if (!read_stream.s.closed) {
     52    return;
     53  }
     54 
     55  used_stdin = true;
     56  rstream_init_fd(&main_loop, &read_stream, STDIN_FILENO);
     57  rstream_start(&read_stream, input_read_cb, NULL);
     58 }
     59 
     60 void input_stop(void)
     61 {
     62  if (read_stream.s.closed) {
     63    return;
     64  }
     65 
     66  rstream_stop(&read_stream);
     67  rstream_may_close(&read_stream);
     68 }
     69 
     70 static void cursorhold_event(void **argv)
     71 {
     72  event_T event = State & MODE_INSERT ? EVENT_CURSORHOLDI : EVENT_CURSORHOLD;
     73  apply_autocmds(event, NULL, NULL, false, curbuf);
     74  did_cursorhold = true;
     75 }
     76 
     77 static void create_cursorhold_event(bool events_enabled)
     78 {
     79  // If events are enabled and the queue has any items, this function should not
     80  // have been called (`inbuf_poll` would return `kTrue`).
     81  // TODO(tarruda): Cursorhold should be implemented as a timer set during the
     82  // `state_check` callback for the states where it can be triggered.
     83  assert(!events_enabled || multiqueue_empty(main_loop.events));
     84  multiqueue_put(main_loop.events, cursorhold_event, NULL);
     85 }
     86 
     87 static void reset_cursorhold_wait(int tb_change_cnt)
     88 {
     89  cursorhold_time = 0;
     90  cursorhold_tb_change_cnt = tb_change_cnt;
     91 }
     92 
     93 /// Reads OS input into `buf`, and consumes pending events while waiting (if `ms != 0`).
     94 ///
     95 /// - Consumes available input received from the OS.
     96 /// - Consumes pending events.
     97 /// - Manages CursorHold events.
     98 /// - Handles EOF conditions.
     99 ///
    100 /// Originally based on the Vim `mch_inchar` function.
    101 ///
    102 /// @param buf Buffer to store consumed input.
    103 /// @param maxlen Maximum bytes to read into `buf`, or 0 to skip reading.
    104 /// @param ms Timeout in milliseconds. -1 for indefinite wait, 0 for no wait.
    105 /// @param tb_change_cnt Used to detect when typeahead changes.
    106 /// @param events (optional) Events to process.
    107 /// @return Bytes read into buf, or 0 if no input was read
    108 int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *events)
    109 {
    110  // This check is needed so that feeding typeahead from RPC can prevent CursorHold.
    111  if (tb_change_cnt != cursorhold_tb_change_cnt) {
    112    reset_cursorhold_wait(tb_change_cnt);
    113  }
    114 
    115 #define TRY_READ() \
    116  do { \
    117    if (maxlen && input_available()) { \
    118      reset_cursorhold_wait(tb_change_cnt); \
    119      assert(maxlen >= 0); \
    120      size_t to_read = MIN((size_t)maxlen, input_available()); \
    121      memcpy(buf, input_read_pos, to_read); \
    122      input_read_pos += to_read; \
    123      /* This is safe because INPUT_BUFFER_SIZE fits in an int. */ \
    124      assert(to_read <= INT_MAX); \
    125      return (int)to_read; \
    126    } \
    127  } while (0)
    128 
    129  TRY_READ();
    130 
    131  // No risk of a UI flood, so disable CTRL-C "interrupt" behavior if it's mapped.
    132  if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) {
    133    ctrl_c_interrupts = false;
    134  }
    135 
    136  TriState result;  ///< inbuf_poll result.
    137  if (ms >= 0) {
    138    if ((result = inbuf_poll(ms, events)) == kFalse) {
    139      return 0;
    140    }
    141  } else {
    142    uint64_t wait_start = os_hrtime();
    143    cursorhold_time = MIN(cursorhold_time, (int)p_ut);
    144    if ((result = inbuf_poll((int)p_ut - cursorhold_time, events)) == kFalse) {
    145      if (read_stream.s.closed && silent_mode) {
    146        // Drained eventloop & initial input; exit silent/batch-mode (-es/-Es).
    147        read_error_exit();
    148      }
    149      reset_cursorhold_wait(tb_change_cnt);
    150      if (trigger_cursorhold() && !typebuf_changed(tb_change_cnt)) {
    151        create_cursorhold_event(events == main_loop.events);
    152      } else {
    153        before_blocking();
    154        result = inbuf_poll(-1, events);
    155      }
    156    } else {
    157      cursorhold_time += (int)((os_hrtime() - wait_start) / 1000000);
    158    }
    159  }
    160 
    161  ctrl_c_interrupts = true;
    162 
    163  // If input was put directly in typeahead buffer bail out here.
    164  if (typebuf_changed(tb_change_cnt)) {
    165    return 0;
    166  }
    167 
    168  TRY_READ();
    169 
    170  // If there are events, return the keys directly
    171  if (maxlen && pending_events(events)) {
    172    return push_event_key(buf, maxlen);
    173  }
    174 
    175  if (result == kNone && ms != 0) {
    176    read_error_exit();
    177  }
    178 
    179  return 0;
    180 
    181 #undef TRY_READ
    182 }
    183 
    184 // Check if a character is available for reading
    185 bool os_char_avail(void)
    186 {
    187  return inbuf_poll(0, NULL) == kTrue;
    188 }
    189 
    190 /// Poll for fast events. `got_int` will be set to `true` if CTRL-C was typed.
    191 ///
    192 /// This invokes a full libuv loop iteration which can be quite costly.
    193 /// Prefer `line_breakcheck()` if called in a busy inner loop.
    194 ///
    195 /// Caller must at least check `got_int` before calling this function again.
    196 /// checking for other low-level input state like `input_available()` might
    197 /// also be relevant (i e to throttle idle processing when user input is
    198 /// available)
    199 void os_breakcheck(void)
    200 {
    201  if (got_int) {
    202    return;
    203  }
    204 
    205  loop_poll_events(&main_loop, 0);
    206 }
    207 
    208 #define BREAKCHECK_SKIP 1000
    209 static int breakcheck_count = 0;
    210 
    211 /// Check for CTRL-C pressed, but only once in a while.
    212 ///
    213 /// Should be used instead of os_breakcheck() for functions that check for
    214 /// each line in the file.  Calling os_breakcheck() each time takes too much
    215 /// time, because it will use system calls to check for input.
    216 void line_breakcheck(void)
    217 {
    218  if (++breakcheck_count >= BREAKCHECK_SKIP) {
    219    breakcheck_count = 0;
    220    os_breakcheck();
    221  }
    222 }
    223 
    224 /// Like line_breakcheck() but check 10 times less often.
    225 void fast_breakcheck(void)
    226 {
    227  if (++breakcheck_count >= BREAKCHECK_SKIP * 10) {
    228    breakcheck_count = 0;
    229    os_breakcheck();
    230  }
    231 }
    232 
    233 /// Like line_breakcheck() but check 100 times less often.
    234 void veryfast_breakcheck(void)
    235 {
    236  if (++breakcheck_count >= BREAKCHECK_SKIP * 100) {
    237    breakcheck_count = 0;
    238    os_breakcheck();
    239  }
    240 }
    241 
    242 /// Test whether a file descriptor refers to a terminal.
    243 ///
    244 /// @param fd File descriptor.
    245 /// @return `true` if file descriptor refers to a terminal.
    246 bool os_isatty(int fd)
    247 {
    248  return uv_guess_handle(fd) == UV_TTY;
    249 }
    250 
    251 size_t input_available(void)
    252 {
    253  return (size_t)(input_write_pos - input_read_pos);
    254 }
    255 
    256 static size_t input_space(void)
    257 {
    258  return (size_t)(input_buffer + INPUT_BUFFER_SIZE - input_write_pos);
    259 }
    260 
    261 void input_enqueue_raw(const char *data, size_t size)
    262 {
    263  if (input_read_pos > input_buffer) {
    264    size_t available = input_available();
    265    memmove(input_buffer, input_read_pos, available);
    266    input_read_pos = input_buffer;
    267    input_write_pos = input_buffer + available;
    268  }
    269 
    270  size_t to_write = MIN(size, input_space());
    271  memcpy(input_write_pos, data, to_write);
    272  input_write_pos += to_write;
    273 }
    274 
    275 size_t input_enqueue(uint64_t chan_id, String keys)
    276 {
    277  current_ui = chan_id;
    278 
    279  const char *ptr = keys.data;
    280  const char *end = ptr + keys.size;
    281 
    282  while (input_space() >= 19 && ptr < end) {
    283    // A "<x>" form occupies at least 1 characters, and produces up
    284    // to 19 characters (1 + 5 * 3 for the char and 3 for a modifier).
    285    // In the case of K_SPECIAL (0x80), 3 bytes are escaped and needed,
    286    // but since the keys are UTF-8, so the first byte cannot be
    287    // K_SPECIAL (0x80).
    288    uint8_t buf[19] = { 0 };
    289    // Do not simplify the keys here. Simplification will be done later.
    290    unsigned new_size
    291      = trans_special(&ptr, (size_t)(end - ptr), (char *)buf, FSK_KEYCODE, true, NULL);
    292 
    293    if (new_size > 0) {
    294      if ((new_size = handle_mouse_event(&ptr, buf, new_size)) > 0) {
    295        input_enqueue_raw((char *)buf, new_size);
    296      }
    297      continue;
    298    }
    299 
    300    if (*ptr == '<') {
    301      const char *old_ptr = ptr;
    302      // Invalid or incomplete key sequence, skip until the next '>' or *end.
    303      do {
    304        ptr++;
    305      } while (ptr < end && *ptr != '>');
    306      if (*ptr != '>') {
    307        // Incomplete key sequence, return without consuming.
    308        ptr = old_ptr;
    309        break;
    310      }
    311      ptr++;
    312      continue;
    313    }
    314 
    315    // copy the character, escaping K_SPECIAL
    316    if ((uint8_t)(*ptr) == K_SPECIAL) {
    317      input_enqueue_raw((char *)&(uint8_t){ K_SPECIAL }, 1);
    318      input_enqueue_raw((char *)&(uint8_t){ KS_SPECIAL }, 1);
    319      input_enqueue_raw((char *)&(uint8_t){ KE_FILLER }, 1);
    320    } else {
    321      input_enqueue_raw(ptr, 1);
    322    }
    323    ptr++;
    324  }
    325 
    326  size_t rv = (size_t)(ptr - keys.data);
    327  process_ctrl_c();
    328  return rv;
    329 }
    330 
    331 static uint8_t check_multiclick(int code, int grid, int row, int col, bool *skip_event)
    332 {
    333  static int orig_num_clicks = 0;
    334  static int orig_mouse_code = 0;
    335  static int orig_mouse_grid = 0;
    336  static int orig_mouse_col = 0;
    337  static int orig_mouse_row = 0;
    338  static uint64_t orig_mouse_time = 0;  // time of previous mouse click
    339 
    340  if (code >= KE_MOUSEDOWN && code <= KE_MOUSERIGHT) {
    341    return 0;
    342  }
    343 
    344  bool no_move = orig_mouse_grid == grid && orig_mouse_col == col && orig_mouse_row == row;
    345 
    346  if (code == KE_MOUSEMOVE) {
    347    if (no_move) {
    348      *skip_event = true;
    349      return 0;
    350    }
    351  } else if (code == KE_LEFTMOUSE || code == KE_RIGHTMOUSE || code == KE_MIDDLEMOUSE
    352             || code == KE_X1MOUSE || code == KE_X2MOUSE) {
    353    // For click events the number of clicks is updated.
    354    uint64_t mouse_time = os_hrtime();    // time of current mouse click (ns)
    355    // Compute the time elapsed since the previous mouse click.
    356    uint64_t timediff = mouse_time - orig_mouse_time;
    357    // Convert 'mousetime' from ms to ns.
    358    uint64_t mouset = (uint64_t)p_mouset * 1000000;
    359    if (code == orig_mouse_code
    360        && no_move
    361        && timediff < mouset
    362        && orig_num_clicks != 4) {
    363      orig_num_clicks++;
    364    } else {
    365      orig_num_clicks = 1;
    366    }
    367    orig_mouse_code = code;
    368    orig_mouse_time = mouse_time;
    369  }
    370  // For drag and release events the number of clicks is kept.
    371 
    372  orig_mouse_grid = grid;
    373  orig_mouse_col = col;
    374  orig_mouse_row = row;
    375 
    376  uint8_t modifiers = 0;
    377  if (code != KE_MOUSEMOVE) {
    378    if (orig_num_clicks == 2) {
    379      modifiers |= MOD_MASK_2CLICK;
    380    } else if (orig_num_clicks == 3) {
    381      modifiers |= MOD_MASK_3CLICK;
    382    } else if (orig_num_clicks == 4) {
    383      modifiers |= MOD_MASK_4CLICK;
    384    }
    385  }
    386  return modifiers;
    387 }
    388 
    389 /// Mouse event handling code (extract row/col if available and detect multiple clicks)
    390 static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufsize)
    391 {
    392  int mouse_code = 0;
    393  int type = 0;
    394 
    395  if (bufsize == 3) {
    396    mouse_code = buf[2];
    397    type = buf[1];
    398  } else if (bufsize == 6) {
    399    // prefixed with K_SPECIAL KS_MODIFIER mod
    400    mouse_code = buf[5];
    401    type = buf[4];
    402  }
    403 
    404  if (type != KS_EXTRA
    405      || !((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
    406           || (mouse_code >= KE_X1MOUSE && mouse_code <= KE_X2RELEASE)
    407           || (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT)
    408           || mouse_code == KE_MOUSEMOVE)) {
    409    return bufsize;
    410  }
    411 
    412  // A <[COL],[ROW]> sequence can follow and will set the mouse_row/mouse_col
    413  // global variables. This is ugly but its how the rest of the code expects to
    414  // find mouse coordinates, and it would be too expensive to refactor this
    415  // now.
    416  int col, row, advance;
    417  if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance) != EOF && advance) {
    418    if (col >= 0 && row >= 0) {
    419      // Make sure the mouse position is valid.  Some terminals may
    420      // return weird values.
    421      if (col >= Columns) {
    422        col = Columns - 1;
    423      }
    424      if (row >= Rows) {
    425        row = Rows - 1;
    426      }
    427      mouse_grid = 0;
    428      mouse_row = row;
    429      mouse_col = col;
    430    }
    431    *ptr += advance;
    432  }
    433 
    434  bool skip_event = false;
    435  uint8_t modifiers = check_multiclick(mouse_code, mouse_grid,
    436                                       mouse_row, mouse_col, &skip_event);
    437  if (skip_event) {
    438    return 0;
    439  }
    440 
    441  if (modifiers) {
    442    if (buf[1] != KS_MODIFIER) {
    443      // no modifiers in the buffer yet, shift the bytes 3 positions
    444      memcpy(buf + 3, buf, 3);
    445      // add the modifier sequence
    446      buf[0] = K_SPECIAL;
    447      buf[1] = KS_MODIFIER;
    448      buf[2] = modifiers;
    449      bufsize += 3;
    450    } else {
    451      buf[2] |= modifiers;
    452    }
    453  }
    454 
    455  return bufsize;
    456 }
    457 
    458 void input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col)
    459 {
    460  bool skip_event = false;
    461  modifier |= check_multiclick(code, grid, row, col, &skip_event);
    462  if (skip_event) {
    463    return;
    464  }
    465  uint8_t buf[7];
    466  uint8_t *p = buf;
    467  if (modifier) {
    468    p[0] = K_SPECIAL;
    469    p[1] = KS_MODIFIER;
    470    p[2] = modifier;
    471    p += 3;
    472  }
    473  p[0] = K_SPECIAL;
    474  p[1] = KS_EXTRA;
    475  p[2] = (uint8_t)code;
    476 
    477  mouse_grid = grid;
    478  mouse_row = row;
    479  mouse_col = col;
    480 
    481  size_t written = 3 + (size_t)(p - buf);
    482  input_enqueue_raw((char *)buf, written);
    483 }
    484 
    485 /// @return true if the main loop is blocked and waiting for input.
    486 bool input_blocking(void)
    487 {
    488  return blocking;
    489 }
    490 
    491 /// Checks for (but does not read) available input, and consumes `main_loop.events` while waiting.
    492 ///
    493 /// @param ms Timeout in milliseconds. -1 for indefinite wait, 0 for no wait.
    494 /// @param events (optional) Queue to check for pending events.
    495 /// @return TriState:
    496 ///   - kTrue: Input/events available
    497 ///   - kFalse: No input/events
    498 ///   - kNone: EOF reached on the input stream
    499 static TriState inbuf_poll(int ms, MultiQueue *events)
    500 {
    501  if (os_input_ready(events)) {
    502    return kTrue;
    503  }
    504 
    505  if (do_profiling == PROF_YES && ms) {
    506    prof_input_start();
    507  }
    508 
    509  if ((ms == -1 || ms > 0) && events != main_loop.events && !input_eof) {
    510    // The pending input provoked a blocking wait. Do special events now. #6247
    511    blocking = true;
    512    multiqueue_process_events(ch_before_blocking_events);
    513  }
    514  DLOG("blocking... events=%s", !!events ? "true" : "false");
    515  LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, os_input_ready(events) || input_eof);
    516  blocking = false;
    517 
    518  if (do_profiling == PROF_YES && ms) {
    519    prof_input_end();
    520  }
    521 
    522  if (os_input_ready(events)) {
    523    return kTrue;
    524  }
    525  return input_eof ? kNone : kFalse;
    526 }
    527 
    528 static size_t input_read_cb(RStream *stream, const char *buf, size_t c, void *data, bool at_eof)
    529 {
    530  if (at_eof) {
    531    input_eof = true;
    532  }
    533 
    534  assert(input_space() >= c);
    535  input_enqueue_raw(buf, c);
    536  return c;
    537 }
    538 
    539 static void process_ctrl_c(void)
    540 {
    541  if (!ctrl_c_interrupts) {
    542    return;
    543  }
    544 
    545  size_t available = input_available();
    546  ssize_t i;
    547  for (i = (ssize_t)available - 1; i >= 0; i--) {  // Reverse-search input for Ctrl_C.
    548    uint8_t c = (uint8_t)input_read_pos[i];
    549    if (c == Ctrl_C
    550        || (c == 'C' && i >= 3
    551            && (uint8_t)input_read_pos[i - 3] == K_SPECIAL
    552            && (uint8_t)input_read_pos[i - 2] == KS_MODIFIER
    553            && (uint8_t)input_read_pos[i - 1] == MOD_MASK_CTRL)) {
    554      input_read_pos[i] = Ctrl_C;
    555      got_int = true;
    556      break;
    557    }
    558  }
    559 
    560  if (got_int && i > 0) {
    561    // Remove all unprocessed input (typeahead) before the CTRL-C.
    562    input_read_pos += i;
    563  }
    564 }
    565 
    566 /// Pushes bytes from the "event" key sequence (KE_EVENT) partially between calls to input_get when
    567 /// `maxlen < 3`.
    568 static int push_event_key(uint8_t *buf, int maxlen)
    569 {
    570  static const uint8_t key[3] = { K_SPECIAL, KS_EXTRA, KE_EVENT };
    571  static int key_idx = 0;
    572  int buf_idx = 0;
    573 
    574  do {
    575    buf[buf_idx++] = key[key_idx++];
    576    key_idx %= 3;
    577  } while (key_idx > 0 && buf_idx < maxlen);
    578 
    579  return buf_idx;
    580 }
    581 
    582 /// Check if there's pending input already in typebuf or `events`
    583 bool os_input_ready(MultiQueue *events)
    584 {
    585  return (typebuf_was_filled             // API call filled typeahead
    586          || input_available()           // Input buffer filled
    587          || pending_events(events));    // Events must be processed
    588 }
    589 
    590 // Exit because of an input read error.
    591 static void read_error_exit(void)
    592  FUNC_ATTR_NORETURN
    593 {
    594  if (silent_mode) {  // Normal way to exit for "nvim -es".
    595    getout(0);
    596  }
    597  preserve_exit(_("Nvim: Error reading input, exiting...\n"));
    598 }
    599 
    600 static bool pending_events(MultiQueue *events)
    601 {
    602  return events && !multiqueue_empty(events);
    603 }