summaryrefslogtreecommitdiffstatshomepage
path: root/src/nvim/os/os_win_console.c
blob: 19870ec2b0d64afc72443b6ac666e4a854d3b2bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <string.h>

#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/os/fs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/os_win_console.h"

#include "os/os_win_console.c.generated.h"

static char origTitle[256] = { 0 };
static HWND hWnd = NULL;
static HICON hOrigIconSmall = NULL;
static HICON hOrigIcon = NULL;

/// Re-enable normal Ctrl-C processing after detached startup.
///
/// On Windows, UV_PROCESS_DETACHED implies CREATE_NEW_PROCESS_GROUP, which
/// disables Ctrl-C handling for the new process. Restore the default behavior
/// once the embedded server has a console so terminal jobs inherit it.
void os_enable_ctrl_c(void)
{
  SetConsoleCtrlHandler(NULL, false);
}

int os_open_conin_fd(void)
{
  const HANDLE conin_handle = CreateFile("CONIN$",
                                         GENERIC_READ | GENERIC_WRITE,
                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                                         (LPSECURITY_ATTRIBUTES)NULL,
                                         OPEN_EXISTING, 0, (HANDLE)NULL);
  assert(conin_handle != INVALID_HANDLE_VALUE);
  int conin_fd = _open_osfhandle((intptr_t)conin_handle, _O_RDONLY);
  assert(conin_fd != -1);
  return conin_fd;
}

void os_clear_hwnd(void)
{
  hWnd = NULL;
}

void os_replace_stdin_to_conin(void)
{
  close(STDIN_FILENO);
  const int conin_fd = os_open_conin_fd();
  assert(conin_fd == STDIN_FILENO);
}

void os_replace_stdout_and_stderr_to_conout(void)
{
  const HANDLE conout_handle =
    CreateFile("CONOUT$",
               GENERIC_READ | GENERIC_WRITE,
               FILE_SHARE_READ | FILE_SHARE_WRITE,
               (LPSECURITY_ATTRIBUTES)NULL,
               OPEN_EXISTING, 0, (HANDLE)NULL);
  assert(conout_handle != INVALID_HANDLE_VALUE);
  close(STDOUT_FILENO);
  const int conout_fd = _open_osfhandle((intptr_t)conout_handle, 0);
  assert(conout_fd == STDOUT_FILENO);
  close(STDERR_FILENO);
  const int conerr_fd = _open_osfhandle((intptr_t)conout_handle, 0);
  assert(conerr_fd == STDERR_FILENO);
}

/// Detach from the current console and switch stdio to a hidden private one.
///
/// Used when an embedded server must outlive its parent console, while keeping
/// CONIN$/CONOUT$ and ConPTY functional for :terminal and stdio writes.
void os_swap_to_hidden_console(void)
{
  FreeConsole();
  AllocConsole();
  ShowWindow(GetConsoleWindow(), SW_HIDE);
  os_enable_ctrl_c();
  os_replace_stdin_to_conin();
  os_replace_stdout_and_stderr_to_conout();
}

/// Resets Windows console icon if we got an original one on startup.
void os_icon_reset(void)
{
  if (hWnd == NULL) {
    return;
  }

  if (hOrigIconSmall) {
    SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hOrigIconSmall);
  }
  if (hOrigIcon) {
    SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hOrigIcon);
  }
}

/// Sets Nvim logo as Windows console icon.
///
/// Saves the original icon so it can be restored at exit.
void os_icon_init(void)
{
  if ((hWnd = GetConsoleWindow()) == NULL) {
    return;
  }

  char *vimruntime = os_getenv("VIMRUNTIME");
  if (vimruntime != NULL) {
    snprintf(NameBuff, MAXPATHL, "%s/neovim.ico", vimruntime);
    if (!os_path_exists(NameBuff)) {
      WLOG("neovim.ico not found: %s", NameBuff);
    } else {
      HICON hVimIcon = LoadImage(NULL, NameBuff, IMAGE_ICON, 64, 64,
                                 LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
      hOrigIconSmall = (HICON)SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hVimIcon);
      hOrigIcon = (HICON)SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hVimIcon);
    }
    xfree(vimruntime);
  }
}

/// Saves the original Windows console title.
void os_title_save(void)
{
  GetConsoleTitle(origTitle, sizeof(origTitle));
}

/// Resets the original Windows console title.
void os_title_reset(void)
{
  SetConsoleTitle(origTitle);
}

#if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
/// Guesses the terminal-type.  Calls SetConsoleMode() and uv_set_vterm_state()
/// if appropriate.
///
/// @param[in,out] term Name of the guessed terminal, statically-allocated
/// @param out_fd stdout file descriptor
void os_tty_guess_term(const char **term, int out_fd)
{
  bool conemu_ansi = strequal(os_getenv_noalloc("ConEmuANSI"), "ON");
  bool vtp = false;

  HANDLE handle = (HANDLE)_get_osfhandle(out_fd);
  DWORD dwMode;
  if (handle != INVALID_HANDLE_VALUE && GetConsoleMode(handle, &dwMode)) {
    dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    if (SetConsoleMode(handle, dwMode)) {
      vtp = true;
    }
  }

  if (*term == NULL) {
    if (vtp) {
      *term = "vtpcon";
    } else if (conemu_ansi) {
      *term = "conemu";
    } else {
      *term = "win32con";
    }
  }

  if (conemu_ansi) {
    uv_tty_set_vterm_state(UV_TTY_SUPPORTED);
  }
}