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 }