aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/pty_conpty_win.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os/pty_conpty_win.c')
-rw-r--r--src/nvim/os/pty_conpty_win.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/nvim/os/pty_conpty_win.c b/src/nvim/os/pty_conpty_win.c
new file mode 100644
index 0000000000..5bcadd6490
--- /dev/null
+++ b/src/nvim/os/pty_conpty_win.c
@@ -0,0 +1,199 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <uv.h>
+
+#include "nvim/vim.h"
+#include "nvim/os/os.h"
+#include "nvim/os/pty_conpty_win.h"
+
+#ifndef EXTENDED_STARTUPINFO_PRESENT
+# define EXTENDED_STARTUPINFO_PRESENT 0x00080000
+#endif
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+# define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+#endif
+
+HRESULT (WINAPI *pCreatePseudoConsole)(COORD, HANDLE, HANDLE, DWORD, HPCON *);
+HRESULT (WINAPI *pResizePseudoConsole)(HPCON, COORD);
+void (WINAPI *pClosePseudoConsole)(HPCON);
+
+bool os_has_conpty_working(void)
+{
+ static TriState has_conpty = kNone;
+ if (has_conpty == kNone) {
+ has_conpty = os_dyn_conpty_init();
+ }
+
+ return has_conpty == kTrue;
+}
+
+TriState os_dyn_conpty_init(void)
+{
+ uv_lib_t kernel;
+ if (uv_dlopen("kernel32.dll", &kernel)) {
+ uv_dlclose(&kernel);
+ return kFalse;
+ }
+ static struct {
+ char *name;
+ FARPROC *ptr;
+ } conpty_entry[] = {
+ { "CreatePseudoConsole", (FARPROC *)&pCreatePseudoConsole },
+ { "ResizePseudoConsole", (FARPROC *)&pResizePseudoConsole },
+ { "ClosePseudoConsole", (FARPROC *)&pClosePseudoConsole },
+ { NULL, NULL }
+ };
+ for (int i = 0;
+ conpty_entry[i].name != NULL && conpty_entry[i].ptr != NULL; i++) {
+ if (uv_dlsym(&kernel, conpty_entry[i].name, (void **)conpty_entry[i].ptr)) {
+ uv_dlclose(&kernel);
+ return kFalse;
+ }
+ }
+ return kTrue;
+}
+
+conpty_t *os_conpty_init(char **in_name, char **out_name,
+ uint16_t width, uint16_t height)
+{
+ static int count = 0;
+ conpty_t *conpty_object = xcalloc(1, sizeof(*conpty_object));
+ const char *emsg = NULL;
+ HANDLE in_read = INVALID_HANDLE_VALUE;
+ HANDLE out_write = INVALID_HANDLE_VALUE;
+ char buf[MAXPATHL];
+ SECURITY_ATTRIBUTES sa = { 0 };
+ const DWORD mode = PIPE_ACCESS_INBOUND
+ | PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
+
+ sa.nLength = sizeof(sa);
+ snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-in-%d-%d",
+ os_get_pid(), count);
+ *in_name = xstrdup(buf);
+ if ((in_read = CreateNamedPipeA(
+ *in_name,
+ mode,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ 1,
+ 0,
+ 0,
+ 30000,
+ &sa)) == INVALID_HANDLE_VALUE) {
+ emsg = "create input pipe failed";
+ goto failed;
+ }
+ snprintf(buf, sizeof(buf), "\\\\.\\pipe\\nvim-term-out-%d-%d",
+ os_get_pid(), count);
+ *out_name = xstrdup(buf);
+ if ((out_write = CreateNamedPipeA(
+ *out_name,
+ mode,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ 1,
+ 0,
+ 0,
+ 30000,
+ &sa)) == INVALID_HANDLE_VALUE) {
+ emsg = "create output pipe failed";
+ goto failed;
+ }
+ assert(width <= SHRT_MAX);
+ assert(height <= SHRT_MAX);
+ COORD size = { (int16_t)width, (int16_t)height };
+ HRESULT hr;
+ hr = pCreatePseudoConsole(size, in_read, out_write, 0, &conpty_object->pty);
+ if (FAILED(hr)) {
+ emsg = "create psudo console failed";
+ goto failed;
+ }
+
+ conpty_object->si_ex.StartupInfo.cb = sizeof(conpty_object->si_ex);
+ size_t bytes_required;
+ InitializeProcThreadAttributeList(NULL, 1, 0, & bytes_required);
+ conpty_object->si_ex.lpAttributeList =
+ (PPROC_THREAD_ATTRIBUTE_LIST)xmalloc(bytes_required);
+ if (!InitializeProcThreadAttributeList(
+ conpty_object->si_ex.lpAttributeList,
+ 1,
+ 0,
+ &bytes_required)) {
+ emsg = "InitializeProcThreadAttributeList failed";
+ goto failed;
+ }
+ if (!UpdateProcThreadAttribute(
+ conpty_object->si_ex.lpAttributeList,
+ 0,
+ PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+ conpty_object->pty,
+ sizeof(conpty_object->pty),
+ NULL,
+ NULL)) {
+ emsg = "UpdateProcThreadAttribute failed";
+ goto failed;
+ }
+ count++;
+ goto finished;
+
+failed:
+ ELOG("os_conpty_init:%s : error code: %d",
+ emsg, os_translate_sys_error((int)GetLastError()));
+ os_conpty_free(conpty_object);
+ conpty_object = NULL;
+finished:
+ if (in_read != INVALID_HANDLE_VALUE) {
+ CloseHandle(in_read);
+ }
+ if (out_write != INVALID_HANDLE_VALUE) {
+ CloseHandle(out_write);
+ }
+ return conpty_object;
+}
+
+bool os_conpty_spawn(conpty_t *conpty_object, HANDLE *process_handle,
+ wchar_t *name, wchar_t *cmd_line, wchar_t *cwd,
+ wchar_t *env)
+{
+ PROCESS_INFORMATION pi = { 0 };
+ if (!CreateProcessW(
+ name,
+ cmd_line,
+ NULL,
+ NULL,
+ false,
+ EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT,
+ env,
+ cwd,
+ &conpty_object->si_ex.StartupInfo,
+ &pi)) {
+ return false;
+ }
+ *process_handle = pi.hProcess;
+ return true;
+}
+
+void os_conpty_set_size(conpty_t *conpty_object,
+ uint16_t width, uint16_t height)
+{
+ assert(width <= SHRT_MAX);
+ assert(height <= SHRT_MAX);
+ COORD size = { (int16_t)width, (int16_t)height };
+ if (pResizePseudoConsole(conpty_object->pty, size) != S_OK) {
+ ELOG("ResizePseudoConsoel failed: error code: %d",
+ os_translate_sys_error((int)GetLastError()));
+ }
+}
+
+void os_conpty_free(conpty_t *conpty_object)
+{
+ if (conpty_object != NULL) {
+ if (conpty_object->si_ex.lpAttributeList != NULL) {
+ DeleteProcThreadAttributeList(conpty_object->si_ex.lpAttributeList);
+ xfree(conpty_object->si_ex.lpAttributeList);
+ }
+ if (conpty_object->pty != NULL) {
+ pClosePseudoConsole(conpty_object->pty);
+ }
+ }
+ xfree(conpty_object);
+}