neovim

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

pty_conpty_win.c (6090B)


      1 #include <uv.h>
      2 
      3 #include "nvim/log.h"
      4 #include "nvim/os/os.h"
      5 #include "nvim/os/pty_conpty_win.h"
      6 #include "nvim/vim_defs.h"
      7 
      8 #ifndef EXTENDED_STARTUPINFO_PRESENT
      9 # define EXTENDED_STARTUPINFO_PRESENT 0x00080000
     10 #endif
     11 #ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
     12 # define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
     13 #endif
     14 
     15 HRESULT(WINAPI *pCreatePseudoConsole)(COORD, HANDLE, HANDLE, DWORD, HPCON *);
     16 HRESULT(WINAPI *pResizePseudoConsole)(HPCON, COORD);
     17 void(WINAPI *pClosePseudoConsole)(HPCON);
     18 
     19 bool os_has_conpty_working(void)
     20 {
     21  static TriState has_conpty = kNone;
     22  if (has_conpty == kNone) {
     23    has_conpty = os_dyn_conpty_init();
     24  }
     25 
     26  return has_conpty == kTrue;
     27 }
     28 
     29 TriState os_dyn_conpty_init(void)
     30 {
     31  uv_lib_t kernel;
     32  if (uv_dlopen("kernel32.dll", &kernel)) {
     33    uv_dlclose(&kernel);
     34    return kFalse;
     35  }
     36  static struct {
     37    char *name;
     38    FARPROC *ptr;
     39  } conpty_entry[] = {
     40    { "CreatePseudoConsole", (FARPROC *)&pCreatePseudoConsole },
     41    { "ResizePseudoConsole", (FARPROC *)&pResizePseudoConsole },
     42    { "ClosePseudoConsole", (FARPROC *)&pClosePseudoConsole },
     43    { NULL, NULL }
     44  };
     45  for (int i = 0;
     46       conpty_entry[i].name != NULL && conpty_entry[i].ptr != NULL; i++) {
     47    if (uv_dlsym(&kernel, conpty_entry[i].name, (void **)conpty_entry[i].ptr)) {
     48      uv_dlclose(&kernel);
     49      return kFalse;
     50    }
     51  }
     52  return kTrue;
     53 }
     54 
     55 conpty_t *os_conpty_init(char **in_name, char **out_name, uint16_t width, uint16_t height)
     56 {
     57  static int count = 0;
     58  conpty_t *conpty_object = xcalloc(1, sizeof(*conpty_object));
     59  const char *emsg = NULL;
     60  HANDLE in_read = INVALID_HANDLE_VALUE;
     61  HANDLE out_write = INVALID_HANDLE_VALUE;
     62  char buf[MAXPATHL];
     63  SECURITY_ATTRIBUTES sa = { 0 };
     64  const DWORD mode = PIPE_ACCESS_INBOUND
     65                     | PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
     66 
     67  sa.nLength = sizeof(sa);
     68  snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-in-%lld-%d",
     69           os_get_pid(), count);
     70  *in_name = xstrdup(buf);
     71  if ((in_read = CreateNamedPipeA(*in_name,
     72                                  mode,
     73                                  PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
     74                                  1,
     75                                  0,
     76                                  0,
     77                                  30000,
     78                                  &sa)) == INVALID_HANDLE_VALUE) {
     79    emsg = "create input pipe failed";
     80    goto failed;
     81  }
     82  snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-out-%lld-%d",
     83           os_get_pid(), count);
     84  *out_name = xstrdup(buf);
     85  if ((out_write = CreateNamedPipeA(*out_name,
     86                                    mode,
     87                                    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
     88                                    1,
     89                                    0,
     90                                    0,
     91                                    30000,
     92                                    &sa)) == INVALID_HANDLE_VALUE) {
     93    emsg = "create output pipe failed";
     94    goto failed;
     95  }
     96  assert(width <= SHRT_MAX);
     97  assert(height <= SHRT_MAX);
     98  COORD size = { (int16_t)width, (int16_t)height };
     99  HRESULT hr;
    100  hr = pCreatePseudoConsole(size, in_read, out_write, 0, &conpty_object->pty);
    101  if (FAILED(hr)) {
    102    emsg = "create pseudo console failed";
    103    goto failed;
    104  }
    105 
    106  conpty_object->si_ex.StartupInfo.cb = sizeof(conpty_object->si_ex);
    107  size_t bytes_required;
    108  InitializeProcThreadAttributeList(NULL, 1, 0,  &bytes_required);
    109  conpty_object->si_ex.lpAttributeList =
    110    (PPROC_THREAD_ATTRIBUTE_LIST)xmalloc(bytes_required);
    111  if (!InitializeProcThreadAttributeList(conpty_object->si_ex.lpAttributeList,
    112                                         1,
    113                                         0,
    114                                         &bytes_required)) {
    115    emsg = "InitializeProcThreadAttributeList failed";
    116    goto failed;
    117  }
    118  if (!UpdateProcThreadAttribute(conpty_object->si_ex.lpAttributeList,
    119                                 0,
    120                                 PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
    121                                 conpty_object->pty,
    122                                 sizeof(conpty_object->pty),
    123                                 NULL,
    124                                 NULL)) {
    125    emsg = "UpdateProcThreadAttribute failed";
    126    goto failed;
    127  }
    128  count++;
    129  goto finished;
    130 
    131 failed:
    132  ELOG("os_conpty_init:%s : error code: %d",
    133       emsg, os_translate_sys_error((int)GetLastError()));
    134  os_conpty_free(conpty_object);
    135  conpty_object = NULL;
    136 finished:
    137  if (in_read != INVALID_HANDLE_VALUE) {
    138    CloseHandle(in_read);
    139  }
    140  if (out_write != INVALID_HANDLE_VALUE) {
    141    CloseHandle(out_write);
    142  }
    143  return conpty_object;
    144 }
    145 
    146 bool os_conpty_spawn(conpty_t *conpty_object, HANDLE *proc_handle, wchar_t *name, wchar_t *cmd_line,
    147                     wchar_t *cwd, wchar_t *env)
    148 {
    149  PROCESS_INFORMATION pi = { 0 };
    150  if (!CreateProcessW(name,
    151                      cmd_line,
    152                      NULL,
    153                      NULL,
    154                      false,
    155                      EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT,
    156                      env,
    157                      cwd,
    158                      &conpty_object->si_ex.StartupInfo,
    159                      &pi)) {
    160    return false;
    161  }
    162  *proc_handle = pi.hProcess;
    163  return true;
    164 }
    165 
    166 void os_conpty_set_size(conpty_t *conpty_object, uint16_t width, uint16_t height)
    167 {
    168  assert(width <= SHRT_MAX);
    169  assert(height <= SHRT_MAX);
    170  COORD size = { (int16_t)width, (int16_t)height };
    171  if (pResizePseudoConsole(conpty_object->pty, size) != S_OK) {
    172    ELOG("ResizePseudoConsole failed: error code: %d",
    173         os_translate_sys_error((int)GetLastError()));
    174  }
    175 }
    176 
    177 void os_conpty_free(conpty_t *conpty_object)
    178 {
    179  if (conpty_object != NULL) {
    180    if (conpty_object->si_ex.lpAttributeList != NULL) {
    181      DeleteProcThreadAttributeList(conpty_object->si_ex.lpAttributeList);
    182      xfree(conpty_object->si_ex.lpAttributeList);
    183    }
    184    if (conpty_object->pty != NULL) {
    185      pClosePseudoConsole(conpty_object->pty);
    186    }
    187  }
    188  xfree(conpty_object);
    189 }