blob: f01d3399e52b39a9e0d0788e1d2f052977da5229 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9/*
10 * os_win32.c
11 *
12 * Used for both the console version and the Win32 GUI. A lot of code is for
Bram Moolenaar4f974752019-02-17 17:44:42 +010013 * the console version only, so there is a lot of "#ifndef FEAT_GUI_MSWIN".
Bram Moolenaar071d4272004-06-13 20:20:40 +000014 *
15 * Win32 (Windows NT and Windows 95) system-dependent routines.
16 * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
17 * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
18 *
19 * George V. Reilly <george@reilly.org> wrote most of this.
20 * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
21 */
22
Bram Moolenaar071d4272004-06-13 20:20:40 +000023#include "vim.h"
24
Bram Moolenaar325b7a22004-07-05 15:58:32 +000025#ifdef FEAT_MZSCHEME
26# include "if_mzsch.h"
27#endif
28
Bram Moolenaar071d4272004-06-13 20:20:40 +000029#include <sys/types.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000030#include <signal.h>
31#include <limits.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010032
Bram Moolenaar0f873732019-12-05 20:28:46 +010033// cproto fails on missing include files
Bram Moolenaar82881492012-11-20 16:53:39 +010034#ifndef PROTO
35# include <process.h>
Bram Moolenaare7bebc42021-02-01 20:50:37 +010036# include <winternl.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010037#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000038
39#undef chdir
40#ifdef __GNUC__
41# ifndef __MINGW32__
42# include <dirent.h>
43# endif
44#else
45# include <direct.h>
46#endif
47
Bram Moolenaar82881492012-11-20 16:53:39 +010048#ifndef PROTO
Bram Moolenaar651fca82021-11-29 20:39:38 +000049# if !defined(FEAT_GUI_MSWIN)
Bram Moolenaar82881492012-11-20 16:53:39 +010050# include <shellapi.h>
51# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000052#endif
53
Bram Moolenaarfb630902016-10-29 14:55:00 +020054#ifdef FEAT_JOB_CHANNEL
55# include <tlhelp32.h>
56#endif
57
Bram Moolenaar071d4272004-06-13 20:20:40 +000058#ifdef __MINGW32__
59# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
60# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
61# endif
62# ifndef RIGHTMOST_BUTTON_PRESSED
63# define RIGHTMOST_BUTTON_PRESSED 0x0002
64# endif
65# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
66# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
67# endif
68# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
69# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
70# endif
71# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
72# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
73# endif
74
75/*
76 * EventFlags
77 */
78# ifndef MOUSE_MOVED
79# define MOUSE_MOVED 0x0001
80# endif
81# ifndef DOUBLE_CLICK
82# define DOUBLE_CLICK 0x0002
83# endif
84#endif
85
Bram Moolenaar0f873732019-12-05 20:28:46 +010086// Record all output and all keyboard & mouse input
87// #define MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +000088
89#ifdef MCH_WRITE_DUMP
90FILE* fdDump = NULL;
91#endif
92
93/*
94 * When generating prototypes for Win32 on Unix, these lines make the syntax
95 * errors disappear. They do not need to be correct.
96 */
97#ifdef PROTO
Bram Moolenaar912bc4a2019-12-01 18:58:11 +010098# define WINAPI
Bram Moolenaar071d4272004-06-13 20:20:40 +000099typedef char * LPCSTR;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000100typedef char * LPWSTR;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101typedef int ACCESS_MASK;
102typedef int BOOL;
Paul Ollis65745772022-06-05 16:55:54 +0100103typedef int BOOLEAN;
104typedef int CALLBACK;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000105typedef int COLORREF;
106typedef int CONSOLE_CURSOR_INFO;
107typedef int COORD;
108typedef int DWORD;
109typedef int HANDLE;
Bram Moolenaaref269542016-01-19 13:22:12 +0100110typedef int LPHANDLE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111typedef int HDC;
112typedef int HFONT;
113typedef int HICON;
114typedef int HINSTANCE;
115typedef int HWND;
116typedef int INPUT_RECORD;
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200117typedef int INT;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000118typedef int KEY_EVENT_RECORD;
119typedef int LOGFONT;
120typedef int LPBOOL;
121typedef int LPCTSTR;
122typedef int LPDWORD;
123typedef int LPSTR;
124typedef int LPTSTR;
125typedef int LPVOID;
126typedef int MOUSE_EVENT_RECORD;
127typedef int PACL;
128typedef int PDWORD;
129typedef int PHANDLE;
130typedef int PRINTDLG;
131typedef int PSECURITY_DESCRIPTOR;
132typedef int PSID;
133typedef int SECURITY_INFORMATION;
134typedef int SHORT;
135typedef int SMALL_RECT;
136typedef int TEXTMETRIC;
137typedef int TOKEN_INFORMATION_CLASS;
138typedef int TRUSTEE;
139typedef int WORD;
140typedef int WCHAR;
141typedef void VOID;
Bram Moolenaar82881492012-11-20 16:53:39 +0100142typedef int BY_HANDLE_FILE_INFORMATION;
Bram Moolenaar32ac8cd2013-07-03 18:49:17 +0200143typedef int SE_OBJECT_TYPE;
144typedef int PSNSECINFO;
145typedef int PSNSECINFOW;
Bram Moolenaarb8e0bdb2014-11-12 16:10:48 +0100146typedef int STARTUPINFO;
147typedef int PROCESS_INFORMATION;
Bram Moolenaard90b6c02016-08-28 18:10:45 +0200148typedef int LPSECURITY_ATTRIBUTES;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100149# define __stdcall // empty
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150#endif
151
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200152#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100153// Win32 Console handles for input and output
Bram Moolenaar071d4272004-06-13 20:20:40 +0000154static HANDLE g_hConIn = INVALID_HANDLE_VALUE;
155static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
156
Bram Moolenaar0f873732019-12-05 20:28:46 +0100157// Win32 Screen buffer,coordinate,console I/O information
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158static SMALL_RECT g_srScrollRegion;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100159static COORD g_coord; // 0-based, but external coords are 1-based
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160
Bram Moolenaar0f873732019-12-05 20:28:46 +0100161// The attribute of the screen when the editor was started
162static WORD g_attrDefault = 7; // lightgray text on black background
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163static WORD g_attrCurrent;
164
Bram Moolenaar0f873732019-12-05 20:28:46 +0100165static int g_fCBrkPressed = FALSE; // set by ctrl-break interrupt
166static int g_fCtrlCPressed = FALSE; // set when ctrl-C or ctrl-break detected
167static int g_fForceExit = FALSE; // set when forcefully exiting
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169static void scroll(unsigned cLines);
170static void set_scroll_region(unsigned left, unsigned top,
171 unsigned right, unsigned bottom);
Bram Moolenaar6982f422019-02-16 16:48:01 +0100172static void set_scroll_region_tb(unsigned top, unsigned bottom);
173static void set_scroll_region_lr(unsigned left, unsigned right);
174static void insert_lines(unsigned cLines);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175static void delete_lines(unsigned cLines);
176static void gotoxy(unsigned x, unsigned y);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000177static void standout(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178static int s_cursor_visible = TRUE;
179static int did_create_conin = FALSE;
Christopher Plewright20b795e2022-12-20 20:01:58 +0000180// The 'input_record_buffer' is an internal dynamic fifo queue of MS-Windows
181// console INPUT_RECORD events that are normally read from the console input
182// buffer. This provides an injection point for testing the low-level handling
183// of INPUT_RECORDs.
184typedef struct input_record_buffer_node_S
185{
186 INPUT_RECORD ir;
187 struct input_record_buffer_node_S *next;
188} input_record_buffer_node_T;
189typedef struct input_record_buffer_S
190{
191 input_record_buffer_node_T *head;
192 input_record_buffer_node_T *tail;
193 int length;
194} input_record_buffer_T;
195static input_record_buffer_T input_record_buffer;
Christopher Plewright20b795e2022-12-20 20:01:58 +0000196static int read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength);
197static int write_input_record_buffer(INPUT_RECORD* irEvents, int nLength);
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200198#endif
199#ifdef FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +0000200static int s_dont_use_vimrun = TRUE;
201static int need_vimrun_warning = FALSE;
202static char *vimrun_path = "vimrun ";
203#endif
204
Bram Moolenaar12b559e2013-06-12 22:41:37 +0200205static int win32_getattrs(char_u *name);
206static int win32_setattrs(char_u *name, int attrs);
207static int win32_set_archive(char_u *name);
208
Bram Moolenaard9ef1b82019-02-13 19:23:10 +0100209static int conpty_working = 0;
Bram Moolenaar57da6982019-09-13 22:30:11 +0200210static int conpty_type = 0;
Bram Moolenaard9ef1b82019-02-13 19:23:10 +0100211static int conpty_stable = 0;
Bram Moolenaar7ed8f592020-04-28 20:44:42 +0200212static int conpty_fix_type = 0;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +0100213static void vtp_flag_init();
214
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200215#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar6902c0e2019-02-16 14:07:37 +0100216static int vtp_working = 0;
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100217static void vtp_init();
218static void vtp_exit();
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100219static void vtp_sgr_bulk(int arg);
220static void vtp_sgr_bulks(int argc, int *argv);
221
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200222static int wt_working = 0;
Christopher Plewright1140b512022-11-12 18:46:05 +0000223static void wt_init(void);
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200224
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +0200225static int g_color_index_bg = 0;
226static int g_color_index_fg = 7;
227
228# ifdef FEAT_TERMGUICOLORS
Christopher Plewright38804d62022-11-09 23:55:52 +0000229static guicolor_T save_console_bg_rgb;
230static guicolor_T save_console_fg_rgb;
231static guicolor_T store_console_bg_rgb;
232static guicolor_T store_console_fg_rgb;
Yasuhiro Matsumotoe42c8da2022-09-01 11:31:45 +0100233static int default_console_color_bg = 0x000000; // black
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +0200234static int default_console_color_fg = 0xc0c0c0; // white
Christopher Plewright38804d62022-11-09 23:55:52 +0000235# define USE_VTP (vtp_working && is_term_win32())
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200236# define USE_WT (wt_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100237# else
238# define USE_VTP 0
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200239# define USE_WT 0
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100240# endif
241
242static void set_console_color_rgb(void);
243static void reset_console_color_rgb(void);
Bram Moolenaardf543822020-01-30 11:53:59 +0100244static void restore_console_color_rgb(void);
Christopher Plewright20b795e2022-12-20 20:01:58 +0000245#endif // !FEAT_GUI_MSWIN || VIMDLL
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100246
Bram Moolenaar0f873732019-12-05 20:28:46 +0100247// This flag is newly created from Windows 10
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100248#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
249# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
250#endif
251
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200252#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +0100253static int suppress_winsize = 1; // don't fiddle with console
Bram Moolenaar071d4272004-06-13 20:20:40 +0000254#endif
255
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200256static char_u *exe_path = NULL;
257
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100258static BOOL win8_or_later = FALSE;
Christopher Plewrightc8b126d2022-12-22 13:45:23 +0000259static BOOL win10_22H2_or_later = FALSE;
Bram Moolenaar9fca1332022-12-22 21:06:41 +0000260#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Christopher Plewrightc8b126d2022-12-22 13:45:23 +0000261static BOOL use_alternate_screen_buffer = FALSE;
Bram Moolenaar9fca1332022-12-22 21:06:41 +0000262#endif
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100263
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100264/*
265 * Get version number including build number
266 */
267typedef BOOL (WINAPI *PfnRtlGetVersion)(LPOSVERSIONINFOW);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100268#define MAKE_VER(major, minor, build) \
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100269 (((major) << 24) | ((minor) << 16) | (build))
270
271 static DWORD
272get_build_number(void)
273{
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100274 OSVERSIONINFOW osver;
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100275 HMODULE hNtdll;
276 PfnRtlGetVersion pRtlGetVersion;
277 DWORD ver = MAKE_VER(0, 0, 0);
278
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100279 osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
Bram Moolenaarcafafb32018-02-22 21:07:09 +0100280 hNtdll = GetModuleHandle("ntdll.dll");
281 if (hNtdll != NULL)
282 {
283 pRtlGetVersion =
284 (PfnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");
285 pRtlGetVersion(&osver);
286 ver = MAKE_VER(min(osver.dwMajorVersion, 255),
287 min(osver.dwMinorVersion, 255),
288 min(osver.dwBuildNumber, 32767));
289 }
290 return ver;
291}
292
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200293#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200294 static BOOL
295is_ambiwidth_event(
296 INPUT_RECORD *ir)
297{
298 return ir->EventType == KEY_EVENT
299 && ir->Event.KeyEvent.bKeyDown
300 && ir->Event.KeyEvent.wRepeatCount == 1
301 && ir->Event.KeyEvent.wVirtualKeyCode == 0x12
302 && ir->Event.KeyEvent.wVirtualScanCode == 0x38
K.Takata972db232022-02-04 10:45:38 +0000303 && ir->Event.KeyEvent.uChar.UnicodeChar == 0
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200304 && ir->Event.KeyEvent.dwControlKeyState == 2;
305}
306
307 static void
308make_ambiwidth_event(
309 INPUT_RECORD *down,
310 INPUT_RECORD *up)
311{
312 down->Event.KeyEvent.wVirtualKeyCode = 0;
313 down->Event.KeyEvent.wVirtualScanCode = 0;
K.Takata972db232022-02-04 10:45:38 +0000314 down->Event.KeyEvent.uChar.UnicodeChar
315 = up->Event.KeyEvent.uChar.UnicodeChar;
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200316 down->Event.KeyEvent.dwControlKeyState = 0;
317}
318
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100319/*
320 * Version of ReadConsoleInput() that works with IME.
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100321 * Works around problems on Windows 8.
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100322 */
323 static BOOL
324read_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100325 HANDLE hInput,
326 INPUT_RECORD *lpBuffer,
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100327 int nLength,
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100328 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100329{
330 enum
331 {
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100332 IRSIZE = 10
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100333 };
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100334 static INPUT_RECORD s_irCache[IRSIZE];
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100335 static DWORD s_dwIndex = 0;
336 static DWORD s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100337 DWORD dwEvents;
Bram Moolenaardd415a62014-02-05 14:02:27 +0100338 int head;
339 int tail;
340 int i;
Bram Moolenaarbc970da2020-04-26 19:00:07 +0200341 static INPUT_RECORD s_irPseudo;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100342
Christopher Plewright20b795e2022-12-20 20:01:58 +0000343 if (s_dwMax == 0 && input_record_buffer.length > 0)
344 {
345 dwEvents = read_input_record_buffer(s_irCache, IRSIZE);
346 s_dwIndex = 0;
347 s_dwMax = dwEvents;
348 }
349
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200350 if (nLength == -2)
351 return (s_dwMax > 0) ? TRUE : FALSE;
352
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100353 if (!win8_or_later)
354 {
355 if (nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200356 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
357 return ReadConsoleInputW(hInput, lpBuffer, 1, &dwEvents);
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100358 }
359
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100360 if (s_dwMax == 0)
361 {
Christopher Plewright38804d62022-11-09 23:55:52 +0000362 if (!vtp_working && nLength == -1)
Bram Moolenaarac360bf2015-09-01 20:31:20 +0200363 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200364 GetNumberOfConsoleInputEvents(hInput, &dwEvents);
365 if (dwEvents == 0 && nLength == -1)
366 return PeekConsoleInputW(hInput, lpBuffer, 1, lpEvents);
367 ReadConsoleInputW(hInput, s_irCache, IRSIZE, &dwEvents);
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100368 s_dwIndex = 0;
369 s_dwMax = dwEvents;
370 if (dwEvents == 0)
371 {
372 *lpEvents = 0;
373 return TRUE;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100374 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100375
Bram Moolenaar06b7b582020-05-30 17:49:25 +0200376 for (i = s_dwIndex; i < (int)s_dwMax - 1; ++i)
377 if (is_ambiwidth_event(&s_irCache[i]))
378 make_ambiwidth_event(&s_irCache[i], &s_irCache[i + 1]);
379
Bram Moolenaardd415a62014-02-05 14:02:27 +0100380 if (s_dwMax > 1)
381 {
382 head = 0;
383 tail = s_dwMax - 1;
384 while (head != tail)
385 {
386 if (s_irCache[head].EventType == WINDOW_BUFFER_SIZE_EVENT
387 && s_irCache[head + 1].EventType
388 == WINDOW_BUFFER_SIZE_EVENT)
389 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100390 // Remove duplicate event to avoid flicker.
Bram Moolenaardd415a62014-02-05 14:02:27 +0100391 for (i = head; i < tail; ++i)
392 s_irCache[i] = s_irCache[i + 1];
393 --tail;
394 continue;
395 }
396 head++;
397 }
398 s_dwMax = tail + 1;
399 }
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100400 }
Bram Moolenaardd415a62014-02-05 14:02:27 +0100401
Bram Moolenaarbc970da2020-04-26 19:00:07 +0200402 if (s_irCache[s_dwIndex].EventType == KEY_EVENT)
403 {
404 if (s_irCache[s_dwIndex].Event.KeyEvent.wRepeatCount > 1)
405 {
406 s_irPseudo = s_irCache[s_dwIndex];
407 s_irPseudo.Event.KeyEvent.wRepeatCount = 1;
408 s_irCache[s_dwIndex].Event.KeyEvent.wRepeatCount--;
409 *lpBuffer = s_irPseudo;
410 *lpEvents = 1;
411 return TRUE;
412 }
413 }
414
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100415 *lpBuffer = s_irCache[s_dwIndex];
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200416 if (!(nLength == -1 || nLength == -2) && ++s_dwIndex >= s_dwMax)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100417 s_dwMax = 0;
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100418 *lpEvents = 1;
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100419 return TRUE;
420}
421
422/*
423 * Version of PeekConsoleInput() that works with IME.
424 */
425 static BOOL
426peek_console_input(
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100427 HANDLE hInput,
428 INPUT_RECORD *lpBuffer,
Bram Moolenaarbd67aac2019-09-21 23:09:04 +0200429 DWORD nLength UNUSED,
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100430 LPDWORD lpEvents)
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100431{
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100432 return read_console_input(hInput, lpBuffer, -1, lpEvents);
Bram Moolenaar3a69e112014-01-10 13:51:42 +0100433}
434
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100435# ifdef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200436 static DWORD
437msg_wait_for_multiple_objects(
438 DWORD nCount,
439 LPHANDLE pHandles,
440 BOOL fWaitAll,
441 DWORD dwMilliseconds,
442 DWORD dwWakeMask)
443{
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100444 if (read_console_input(NULL, NULL, -2, NULL))
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200445 return WAIT_OBJECT_0;
446 return MsgWaitForMultipleObjects(nCount, pHandles, fWaitAll,
447 dwMilliseconds, dwWakeMask);
448}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100449# endif
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200450
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100451# ifndef FEAT_CLIENTSERVER
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200452 static DWORD
453wait_for_single_object(
454 HANDLE hHandle,
455 DWORD dwMilliseconds)
456{
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100457 if (read_console_input(NULL, NULL, -2, NULL))
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200458 return WAIT_OBJECT_0;
459 return WaitForSingleObject(hHandle, dwMilliseconds);
460}
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100461# endif
Christopher Plewright20b795e2022-12-20 20:01:58 +0000462#endif // !FEAT_GUI_MSWIN || VIMDLL
Bram Moolenaarbb86ebb2015-08-04 19:27:05 +0200463
Bram Moolenaar071d4272004-06-13 20:20:40 +0000464 static void
465get_exe_name(void)
466{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100467 // Maximum length of $PATH is more than MAXPATHL. 8191 is often mentioned
468 // as the maximum length that works (plus a NUL byte).
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100469#define MAX_ENV_PATH_LEN 8192
470 char temp[MAX_ENV_PATH_LEN];
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200471 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472
473 if (exe_name == NULL)
474 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100475 // store the name of the executable, may be used for $VIM
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100476 GetModuleFileName(NULL, temp, MAX_ENV_PATH_LEN - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 if (*temp != NUL)
478 exe_name = FullName_save((char_u *)temp, FALSE);
479 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000480
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200481 if (exe_path == NULL && exe_name != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000482 {
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200483 exe_path = vim_strnsave(exe_name, gettail_sep(exe_name) - exe_name);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200484 if (exe_path != NULL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000485 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100486 // Append our starting directory to $PATH, so that when doing
487 // "!xxd" it's found in our starting directory. Needed because
488 // SearchPath() also looks there.
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200489 p = mch_getenv("PATH");
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100490 if (p == NULL
491 || STRLEN(p) + STRLEN(exe_path) + 2 < MAX_ENV_PATH_LEN)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200492 {
Bram Moolenaar27d9ece2010-11-10 15:37:05 +0100493 if (p == NULL || *p == NUL)
494 temp[0] = NUL;
495 else
496 {
497 STRCPY(temp, p);
498 STRCAT(temp, ";");
499 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200500 STRCAT(temp, exe_path);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100501 vim_setenv((char_u *)"PATH", (char_u *)temp);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200502 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000503 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000504 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505}
506
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200507/*
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100508 * Unescape characters in "p" that appear in "escaped".
509 */
510 static void
511unescape_shellxquote(char_u *p, char_u *escaped)
512{
Bram Moolenaar4336cdf2012-02-29 13:58:47 +0100513 int l = (int)STRLEN(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100514 int n;
515
516 while (*p != NUL)
517 {
518 if (*p == '^' && vim_strchr(escaped, p[1]) != NULL)
519 mch_memmove(p, p + 1, l--);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100520 n = (*mb_ptr2len)(p);
Bram Moolenaar6b707b42012-02-21 21:22:44 +0100521 p += n;
522 l -= n;
523 }
524}
525
526/*
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200527 * Load library "name".
528 */
529 HINSTANCE
K.Takatad68b2fc2022-02-12 11:18:37 +0000530vimLoadLib(const char *name)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200531{
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200532 HINSTANCE dll = NULL;
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200533
Bram Moolenaar3d0e7a92021-05-01 17:46:03 +0200534 // No need to load any library when registering OLE.
535 if (found_register_arg)
536 return dll;
537
Bram Moolenaar0f873732019-12-05 20:28:46 +0100538 // NOTE: Do not use mch_dirname() and mch_chdir() here, they may call
539 // vimLoadLib() recursively, which causes a stack overflow.
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200540 if (exe_path == NULL)
541 get_exe_name();
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200542 if (exe_path != NULL)
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200543 {
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200544 WCHAR old_dirw[MAXPATHL];
545
546 if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0)
547 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100548 // Change directory to where the executable is, both to make
549 // sure we find a .dll there and to avoid looking for a .dll
550 // in the current directory.
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100551 SetCurrentDirectory((LPCSTR)exe_path);
Bram Moolenaar17aa8cc2012-10-21 21:38:45 +0200552 dll = LoadLibrary(name);
553 SetCurrentDirectoryW(old_dirw);
554 return dll;
555 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200556 }
557 return dll;
558}
559
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200560#if defined(VIMDLL) || defined(PROTO)
561/*
562 * Check if the current executable file is for the GUI subsystem.
563 */
564 int
565mch_is_gui_executable(void)
566{
567 PBYTE pImage = (PBYTE)GetModuleHandle(NULL);
568 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)pImage;
569 PIMAGE_NT_HEADERS pPE;
570
571 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
572 return FALSE;
573 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
574 if (pPE->Signature != IMAGE_NT_SIGNATURE)
575 return FALSE;
576 if (pPE->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)
577 return TRUE;
578 return FALSE;
579}
580#endif
581
Bram Moolenaar63ff72a2022-02-07 13:54:01 +0000582#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) \
583 || defined(FEAT_PYTHON3) || defined(PROTO)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100584/*
585 * Get related information about 'funcname' which is imported by 'hInst'.
586 * If 'info' is 0, return the function address.
587 * If 'info' is 1, return the module name which the function is imported from.
Bram Moolenaar63ff72a2022-02-07 13:54:01 +0000588 * If 'info' is 2, hook the function with 'ptr', and return the original
589 * function address.
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100590 */
591 static void *
Bram Moolenaar63ff72a2022-02-07 13:54:01 +0000592get_imported_func_info(HINSTANCE hInst, const char *funcname, int info,
593 const void *ptr)
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100594{
595 PBYTE pImage = (PBYTE)hInst;
596 PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst;
597 PIMAGE_NT_HEADERS pPE;
598 PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
Bram Moolenaar0f873732019-12-05 20:28:46 +0100599 PIMAGE_THUNK_DATA pIAT; // Import Address Table
600 PIMAGE_THUNK_DATA pINT; // Import Name Table
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100601 PIMAGE_IMPORT_BY_NAME pImpName;
602
603 if (pDOS->e_magic != IMAGE_DOS_SIGNATURE)
604 return NULL;
605 pPE = (PIMAGE_NT_HEADERS)(pImage + pDOS->e_lfanew);
606 if (pPE->Signature != IMAGE_NT_SIGNATURE)
607 return NULL;
608 pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pImage
609 + pPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
610 .VirtualAddress);
611 for (; pImpDesc->FirstThunk; ++pImpDesc)
612 {
613 if (!pImpDesc->OriginalFirstThunk)
614 continue;
615 pIAT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->FirstThunk);
616 pINT = (PIMAGE_THUNK_DATA)(pImage + pImpDesc->OriginalFirstThunk);
617 for (; pIAT->u1.Function; ++pIAT, ++pINT)
618 {
619 if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal))
620 continue;
621 pImpName = (PIMAGE_IMPORT_BY_NAME)(pImage
622 + (UINT_PTR)(pINT->u1.AddressOfData));
623 if (strcmp((char *)pImpName->Name, funcname) == 0)
624 {
Bram Moolenaar63ff72a2022-02-07 13:54:01 +0000625 void *original;
626 DWORD old, new = PAGE_READWRITE;
627
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100628 switch (info)
629 {
630 case 0:
631 return (void *)pIAT->u1.Function;
632 case 1:
633 return (void *)(pImage + pImpDesc->Name);
Bram Moolenaar63ff72a2022-02-07 13:54:01 +0000634 case 2:
635 original = (void *)pIAT->u1.Function;
636 VirtualProtect(&pIAT->u1.Function, sizeof(void *),
637 new, &old);
638 pIAT->u1.Function = (UINT_PTR)ptr;
639 VirtualProtect(&pIAT->u1.Function, sizeof(void *),
640 old, &new);
641 return original;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100642 default:
643 return NULL;
644 }
645 }
646 }
647 }
648 return NULL;
649}
650
651/*
652 * Get the module handle which 'funcname' in 'hInst' is imported from.
653 */
654 HINSTANCE
655find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname)
656{
657 char *modulename;
658
Bram Moolenaar63ff72a2022-02-07 13:54:01 +0000659 modulename = (char *)get_imported_func_info(hInst, funcname, 1, NULL);
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100660 if (modulename != NULL)
661 return GetModuleHandleA(modulename);
662 return NULL;
663}
664
665/*
666 * Get the address of 'funcname' which is imported by 'hInst' DLL.
667 */
668 void *
669get_dll_import_func(HINSTANCE hInst, const char *funcname)
670{
Bram Moolenaar63ff72a2022-02-07 13:54:01 +0000671 return get_imported_func_info(hInst, funcname, 0, NULL);
672}
673
674/*
675 * Hook the function named 'funcname' which is imported by 'hInst' DLL,
676 * and return the original function address.
677 */
678 void *
679hook_dll_import_func(HINSTANCE hInst, const char *funcname, const void *hook)
680{
681 return get_imported_func_info(hInst, funcname, 2, hook);
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100682}
683#endif
684
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685#if defined(DYNAMIC_GETTEXT) || defined(PROTO)
686# ifndef GETTEXT_DLL
687# define GETTEXT_DLL "libintl.dll"
Bram Moolenaar7554c542018-10-06 15:03:15 +0200688# define GETTEXT_DLL_ALT1 "libintl-8.dll"
689# define GETTEXT_DLL_ALT2 "intl.dll"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000690# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +0100691// Dummy functions
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000692static char *null_libintl_gettext(const char *);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200693static char *null_libintl_ngettext(const char *, const char *, unsigned long n);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000694static char *null_libintl_textdomain(const char *);
695static char *null_libintl_bindtextdomain(const char *, const char *);
696static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100697static int null_libintl_wputenv(const wchar_t *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200699static HINSTANCE hLibintlDLL = NULL;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000700char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200701char *(*dyn_libintl_ngettext)(const char *, const char *, unsigned long n)
702 = null_libintl_ngettext;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000703char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
704char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000705 = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000706char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
707 = null_libintl_bind_textdomain_codeset;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100708int (*dyn_libintl_wputenv)(const wchar_t *) = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709
710 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100711dyn_libintl_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000712{
713 int i;
714 static struct
715 {
716 char *name;
717 FARPROC *ptr;
718 } libintl_entry[] =
719 {
720 {"gettext", (FARPROC*)&dyn_libintl_gettext},
Bram Moolenaaree695f72016-08-03 22:08:45 +0200721 {"ngettext", (FARPROC*)&dyn_libintl_ngettext},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000722 {"textdomain", (FARPROC*)&dyn_libintl_textdomain},
723 {"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain},
724 {NULL, NULL}
725 };
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100726 HINSTANCE hmsvcrt;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727
Bram Moolenaar7554c542018-10-06 15:03:15 +0200728 // No need to initialize twice.
729 if (hLibintlDLL != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730 return 1;
Bram Moolenaar7554c542018-10-06 15:03:15 +0200731 // Load gettext library (libintl.dll and other names).
Bram Moolenaar923e43b2016-01-28 15:07:38 +0100732 hLibintlDLL = vimLoadLib(GETTEXT_DLL);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100733# ifdef GETTEXT_DLL_ALT1
Bram Moolenaar286eacd2016-01-16 18:05:50 +0100734 if (!hLibintlDLL)
Bram Moolenaar7554c542018-10-06 15:03:15 +0200735 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100736# endif
737# ifdef GETTEXT_DLL_ALT2
Bram Moolenaar7554c542018-10-06 15:03:15 +0200738 if (!hLibintlDLL)
739 hLibintlDLL = vimLoadLib(GETTEXT_DLL_ALT2);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100740# endif
Bram Moolenaar938ee832016-01-24 15:36:03 +0100741 if (!hLibintlDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200743 if (p_verbose > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000744 {
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200745 verbose_enter();
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000746 semsg(_(e_could_not_load_library_str_str), GETTEXT_DLL, GetWin32Error());
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200747 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748 }
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200749 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750 }
751 for (i = 0; libintl_entry[i].name != NULL
752 && libintl_entry[i].ptr != NULL; ++i)
753 {
K.Takata54119102022-02-03 13:33:03 +0000754 if ((*libintl_entry[i].ptr = GetProcAddress(hLibintlDLL,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000755 libintl_entry[i].name)) == NULL)
756 {
757 dyn_libintl_end();
758 if (p_verbose > 0)
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000759 {
760 verbose_enter();
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000761 semsg(_(e_could_not_load_library_function_str), libintl_entry[i].name);
Bram Moolenaara04f10b2005-05-31 22:09:46 +0000762 verbose_leave();
763 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 return 0;
765 }
766 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000767
Bram Moolenaar0f873732019-12-05 20:28:46 +0100768 // The bind_textdomain_codeset() function is optional.
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100769 dyn_libintl_bind_textdomain_codeset = (char *(*)(const char *, const char *))
770 GetProcAddress(hLibintlDLL, "bind_textdomain_codeset");
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000771 if (dyn_libintl_bind_textdomain_codeset == NULL)
772 dyn_libintl_bind_textdomain_codeset =
773 null_libintl_bind_textdomain_codeset;
774
Bram Moolenaar0f873732019-12-05 20:28:46 +0100775 // _wputenv() function for the libintl.dll is optional.
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100776 hmsvcrt = find_imported_module_by_funcname(hLibintlDLL, "getenv");
777 if (hmsvcrt != NULL)
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +0100778 dyn_libintl_wputenv = (int (*)(const wchar_t *))
779 GetProcAddress(hmsvcrt, "_wputenv");
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100780 if (dyn_libintl_wputenv == NULL || dyn_libintl_wputenv == _wputenv)
781 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar972c3b82017-01-12 21:44:49 +0100782
Bram Moolenaar071d4272004-06-13 20:20:40 +0000783 return 1;
784}
785
786 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100787dyn_libintl_end(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788{
789 if (hLibintlDLL)
790 FreeLibrary(hLibintlDLL);
791 hLibintlDLL = NULL;
792 dyn_libintl_gettext = null_libintl_gettext;
Bram Moolenaaree695f72016-08-03 22:08:45 +0200793 dyn_libintl_ngettext = null_libintl_ngettext;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794 dyn_libintl_textdomain = null_libintl_textdomain;
795 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000796 dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100797 dyn_libintl_wputenv = null_libintl_wputenv;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798}
799
800 static char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000801null_libintl_gettext(const char *msgid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000802{
803 return (char*)msgid;
804}
805
806 static char *
Bram Moolenaaree695f72016-08-03 22:08:45 +0200807null_libintl_ngettext(
808 const char *msgid,
809 const char *msgid_plural,
810 unsigned long n)
811{
Bram Moolenaarc90f2ae2016-08-04 22:00:15 +0200812 return (char *)(n == 1 ? msgid : msgid_plural);
Bram Moolenaaree695f72016-08-03 22:08:45 +0200813}
814
Bram Moolenaaree695f72016-08-03 22:08:45 +0200815 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100816null_libintl_bindtextdomain(
817 const char *domainname UNUSED,
818 const char *dirname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000819{
820 return NULL;
821}
822
823 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100824null_libintl_bind_textdomain_codeset(
825 const char *domainname UNUSED,
826 const char *codeset UNUSED)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000827{
828 return NULL;
829}
830
831 static char *
Bram Moolenaar1266d672017-02-01 13:43:36 +0100832null_libintl_textdomain(const char *domainname UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000833{
834 return NULL;
835}
836
Bram Moolenaare0ab9792017-08-02 23:18:25 +0200837 static int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100838null_libintl_wputenv(const wchar_t *envstring UNUSED)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +0100839{
840 return 0;
841}
842
Bram Moolenaar0f873732019-12-05 20:28:46 +0100843#endif // DYNAMIC_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844
Bram Moolenaar0f873732019-12-05 20:28:46 +0100845// This symbol is not defined in older versions of the SDK or Visual C++
Bram Moolenaar071d4272004-06-13 20:20:40 +0000846
847#ifndef VER_PLATFORM_WIN32_WINDOWS
848# define VER_PLATFORM_WIN32_WINDOWS 1
849#endif
850
Bram Moolenaar071d4272004-06-13 20:20:40 +0000851#ifdef HAVE_ACL
Bram Moolenaar82881492012-11-20 16:53:39 +0100852# ifndef PROTO
853# include <aclapi.h>
854# endif
Bram Moolenaar27515922013-06-29 15:36:26 +0200855# ifndef PROTECTED_DACL_SECURITY_INFORMATION
856# define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
857# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000858#endif
859
Bram Moolenaar27515922013-06-29 15:36:26 +0200860#ifdef HAVE_ACL
861/*
862 * Enables or disables the specified privilege.
863 */
864 static BOOL
865win32_enable_privilege(LPTSTR lpszPrivilege, BOOL bEnable)
866{
Bram Moolenaarb0d5c962014-01-12 13:24:51 +0100867 BOOL bResult;
868 LUID luid;
869 HANDLE hToken;
870 TOKEN_PRIVILEGES tokenPrivileges;
Bram Moolenaar27515922013-06-29 15:36:26 +0200871
872 if (!OpenProcessToken(GetCurrentProcess(),
873 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
874 return FALSE;
875
876 if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
877 {
878 CloseHandle(hToken);
879 return FALSE;
880 }
881
Bram Moolenaar45500912014-07-09 20:51:07 +0200882 tokenPrivileges.PrivilegeCount = 1;
Bram Moolenaar27515922013-06-29 15:36:26 +0200883 tokenPrivileges.Privileges[0].Luid = luid;
884 tokenPrivileges.Privileges[0].Attributes = bEnable ?
885 SE_PRIVILEGE_ENABLED : 0;
886
887 bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges,
888 sizeof(TOKEN_PRIVILEGES), NULL, NULL);
889
890 CloseHandle(hToken);
891
892 return bResult && GetLastError() == ERROR_SUCCESS;
893}
894#endif
895
Bram Moolenaar29d2f452020-12-04 19:42:52 +0100896#ifdef _MSC_VER
897// Suppress the deprecation warning for using GetVersionEx().
898// It is needed for implementing "windowsversion()".
899# pragma warning(push)
900# pragma warning(disable: 4996)
901#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000902/*
Bram Moolenaar1e1d2e82020-05-18 20:17:02 +0200903 * Set "win8_or_later" and fill in "windowsVersion" if possible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 */
905 void
906PlatformId(void)
907{
908 static int done = FALSE;
909
910 if (!done)
911 {
912 OSVERSIONINFO ovi;
913
914 ovi.dwOSVersionInfoSize = sizeof(ovi);
915 GetVersionEx(&ovi);
916
Bram Moolenaar1e1d2e82020-05-18 20:17:02 +0200917#ifdef FEAT_EVAL
Bram Moolenaar0c1e3742019-12-27 13:49:24 +0100918 vim_snprintf(windowsVersion, sizeof(windowsVersion), "%d.%d",
919 (int)ovi.dwMajorVersion, (int)ovi.dwMinorVersion);
Bram Moolenaar1e1d2e82020-05-18 20:17:02 +0200920#endif
Bram Moolenaarf50eb782014-02-05 13:36:54 +0100921 if ((ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2)
922 || ovi.dwMajorVersion > 6)
923 win8_or_later = TRUE;
924
Christopher Plewrightc8b126d2022-12-22 13:45:23 +0000925 if ((ovi.dwMajorVersion == 10 && ovi.dwBuildNumber >= 19045)
Christopher Plewright1140b512022-11-12 18:46:05 +0000926 || ovi.dwMajorVersion > 10)
Christopher Plewrightc8b126d2022-12-22 13:45:23 +0000927 win10_22H2_or_later = TRUE;
Christopher Plewright1140b512022-11-12 18:46:05 +0000928
Bram Moolenaar071d4272004-06-13 20:20:40 +0000929#ifdef HAVE_ACL
Bram Moolenaar0f873732019-12-05 20:28:46 +0100930 // Enable privilege for getting or setting SACLs.
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200931 win32_enable_privilege(SE_SECURITY_NAME, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000932#endif
933 done = TRUE;
934 }
935}
Bram Moolenaar29d2f452020-12-04 19:42:52 +0100936#ifdef _MSC_VER
937# pragma warning(pop)
938#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000939
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200940#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000941
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100942# define SHIFT (SHIFT_PRESSED)
943# define CTRL (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
944# define ALT (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
945# define ALT_GR (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946
947
Bram Moolenaar0f873732019-12-05 20:28:46 +0100948// When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
949// We map function keys to their ANSI terminal equivalents, as produced
950// by ANSI.SYS, for compatibility with the MS-DOS version of Vim. Any
951// ANSI key with a value >= '\300' is nonstandard, but provided anyway
952// so that the user can have access to all SHIFT-, CTRL-, and ALT-
953// combinations of function/arrow/etc keys.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954
Bram Moolenaard6f676d2005-06-01 21:51:55 +0000955static const struct
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956{
957 WORD wVirtKey;
958 BOOL fAnsiKey;
959 int chAlone;
960 int chShift;
961 int chCtrl;
962 int chAlt;
963} VirtKeyMap[] =
964{
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200965// Key ANSI alone shift ctrl alt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 { VK_ESCAPE,FALSE, ESC, ESC, ESC, ESC, },
967
968 { VK_F1, TRUE, ';', 'T', '^', 'h', },
969 { VK_F2, TRUE, '<', 'U', '_', 'i', },
970 { VK_F3, TRUE, '=', 'V', '`', 'j', },
971 { VK_F4, TRUE, '>', 'W', 'a', 'k', },
972 { VK_F5, TRUE, '?', 'X', 'b', 'l', },
973 { VK_F6, TRUE, '@', 'Y', 'c', 'm', },
974 { VK_F7, TRUE, 'A', 'Z', 'd', 'n', },
975 { VK_F8, TRUE, 'B', '[', 'e', 'o', },
976 { VK_F9, TRUE, 'C', '\\', 'f', 'p', },
977 { VK_F10, TRUE, 'D', ']', 'g', 'q', },
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200978 { VK_F11, TRUE, '\205', '\207', '\211', '\213', },
979 { VK_F12, TRUE, '\206', '\210', '\212', '\214', },
Bram Moolenaar071d4272004-06-13 20:20:40 +0000980
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200981 { VK_HOME, TRUE, 'G', '\302', 'w', '\303', },
982 { VK_UP, TRUE, 'H', '\304', '\305', '\306', },
983 { VK_PRIOR, TRUE, 'I', '\307', '\204', '\310', }, // PgUp
984 { VK_LEFT, TRUE, 'K', '\311', 's', '\312', },
985 { VK_RIGHT, TRUE, 'M', '\313', 't', '\314', },
986 { VK_END, TRUE, 'O', '\315', 'u', '\316', },
987 { VK_DOWN, TRUE, 'P', '\317', '\320', '\321', },
988 { VK_NEXT, TRUE, 'Q', '\322', 'v', '\323', }, // PgDn
989 { VK_INSERT,TRUE, 'R', '\324', '\325', '\326', },
990 { VK_DELETE,TRUE, 'S', '\327', '\330', '\331', },
Bram Moolenaarb70a47b2019-03-30 22:11:21 +0100991 { VK_BACK, TRUE, 'x', 'y', 'z', '{', }, // Backspace
Bram Moolenaar071d4272004-06-13 20:20:40 +0000992
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200993 { VK_SNAPSHOT,TRUE, 0, 0, 0, 'r', }, // PrtScrn
Bram Moolenaar071d4272004-06-13 20:20:40 +0000994
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100995# if 0
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +0200996 // Most people don't have F13-F20, but what the hell...
997 { VK_F13, TRUE, '\332', '\333', '\334', '\335', },
998 { VK_F14, TRUE, '\336', '\337', '\340', '\341', },
999 { VK_F15, TRUE, '\342', '\343', '\344', '\345', },
1000 { VK_F16, TRUE, '\346', '\347', '\350', '\351', },
1001 { VK_F17, TRUE, '\352', '\353', '\354', '\355', },
1002 { VK_F18, TRUE, '\356', '\357', '\360', '\361', },
1003 { VK_F19, TRUE, '\362', '\363', '\364', '\365', },
1004 { VK_F20, TRUE, '\366', '\367', '\370', '\371', },
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001005# endif
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001006 { VK_ADD, TRUE, 'N', 'N', 'N', 'N', }, // keyp '+'
1007 { VK_SUBTRACT, TRUE,'J', 'J', 'J', 'J', }, // keyp '-'
1008 // { VK_DIVIDE, TRUE,'N', 'N', 'N', 'N', }, // keyp '/'
1009 { VK_MULTIPLY, TRUE,'7', '7', '7', '7', }, // keyp '*'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001010
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02001011 { VK_NUMPAD0,TRUE, '\332', '\333', '\334', '\335', },
1012 { VK_NUMPAD1,TRUE, '\336', '\337', '\340', '\341', },
1013 { VK_NUMPAD2,TRUE, '\342', '\343', '\344', '\345', },
1014 { VK_NUMPAD3,TRUE, '\346', '\347', '\350', '\351', },
1015 { VK_NUMPAD4,TRUE, '\352', '\353', '\354', '\355', },
1016 { VK_NUMPAD5,TRUE, '\356', '\357', '\360', '\361', },
1017 { VK_NUMPAD6,TRUE, '\362', '\363', '\364', '\365', },
1018 { VK_NUMPAD7,TRUE, '\366', '\367', '\370', '\371', },
1019 { VK_NUMPAD8,TRUE, '\372', '\373', '\374', '\375', },
1020 // Sorry, out of number space! <negri>
Bram Moolenaarb70a47b2019-03-30 22:11:21 +01001021 { VK_NUMPAD9,TRUE, '\376', '\377', '|', '}', },
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022};
1023
1024
Bram Moolenaar0f873732019-12-05 20:28:46 +01001025/*
1026 * The return code indicates key code size.
1027 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001028 static int
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029win32_kbd_patch_key(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001030 KEY_EVENT_RECORD *pker)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001031{
1032 UINT uMods = pker->dwControlKeyState;
1033 static int s_iIsDead = 0;
1034 static WORD awAnsiCode[2];
1035 static BYTE abKeystate[256];
1036
1037
1038 if (s_iIsDead == 2)
1039 {
K.Takata972db232022-02-04 10:45:38 +00001040 pker->uChar.UnicodeChar = (WCHAR) awAnsiCode[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001041 s_iIsDead = 0;
1042 return 1;
1043 }
1044
Christopher Plewright20b795e2022-12-20 20:01:58 +00001045 if (pker->uChar.UnicodeChar > 0 && pker->uChar.UnicodeChar < 0xfffd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046 return 1;
1047
Bram Moolenaara80faa82020-04-12 19:37:17 +02001048 CLEAR_FIELD(abKeystate);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049
Bram Moolenaar0f873732019-12-05 20:28:46 +01001050 // Clear any pending dead keys
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001051 ToUnicode(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052
1053 if (uMods & SHIFT_PRESSED)
1054 abKeystate[VK_SHIFT] = 0x80;
1055 if (uMods & CAPSLOCK_ON)
1056 abKeystate[VK_CAPITAL] = 1;
1057
1058 if ((uMods & ALT_GR) == ALT_GR)
1059 {
1060 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
1061 abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
1062 }
1063
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001064 s_iIsDead = ToUnicode(pker->wVirtualKeyCode, pker->wVirtualScanCode,
1065 abKeystate, awAnsiCode, 2, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066
1067 if (s_iIsDead > 0)
K.Takata972db232022-02-04 10:45:38 +00001068 pker->uChar.UnicodeChar = (WCHAR) awAnsiCode[0];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069
1070 return s_iIsDead;
1071}
1072
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073static BOOL g_fJustGotFocus = FALSE;
1074
1075/*
1076 * Decode a KEY_EVENT into one or two keystrokes
1077 */
1078 static BOOL
1079decode_key_event(
1080 KEY_EVENT_RECORD *pker,
Bram Moolenaarac360bf2015-09-01 20:31:20 +02001081 WCHAR *pch,
1082 WCHAR *pch2,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001083 int *pmodifiers,
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02001084 BOOL fDoPost UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085{
1086 int i;
1087 const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
1088
1089 *pch = *pch2 = NUL;
1090 g_fJustGotFocus = FALSE;
1091
Bram Moolenaar0f873732019-12-05 20:28:46 +01001092 // ignore key up events
Bram Moolenaar071d4272004-06-13 20:20:40 +00001093 if (!pker->bKeyDown)
1094 return FALSE;
1095
Bram Moolenaar0f873732019-12-05 20:28:46 +01001096 // ignore some keystrokes
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097 switch (pker->wVirtualKeyCode)
1098 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001099 // modifiers
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100 case VK_SHIFT:
1101 case VK_CONTROL:
Bram Moolenaar0f873732019-12-05 20:28:46 +01001102 case VK_MENU: // Alt key
Bram Moolenaar071d4272004-06-13 20:20:40 +00001103 return FALSE;
1104
1105 default:
1106 break;
1107 }
1108
Bram Moolenaar0f873732019-12-05 20:28:46 +01001109 // Shift-TAB
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 if (pker->wVirtualKeyCode == VK_TAB && (nModifs & SHIFT_PRESSED))
1111 {
1112 *pch = K_NUL;
1113 *pch2 = '\017';
1114 return TRUE;
1115 }
1116
K.Takataeeec2542021-06-02 13:28:16 +02001117 for (i = ARRAY_LENGTH(VirtKeyMap); --i >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001118 {
1119 if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
1120 {
1121 if (nModifs == 0)
1122 *pch = VirtKeyMap[i].chAlone;
1123 else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
1124 *pch = VirtKeyMap[i].chShift;
1125 else if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0)
1126 *pch = VirtKeyMap[i].chCtrl;
1127 else if ((nModifs & ALT) != 0 && (nModifs & ~ALT) == 0)
1128 *pch = VirtKeyMap[i].chAlt;
1129
1130 if (*pch != 0)
1131 {
1132 if (VirtKeyMap[i].fAnsiKey)
1133 {
1134 *pch2 = *pch;
1135 *pch = K_NUL;
1136 }
1137
1138 return TRUE;
1139 }
1140 }
1141 }
1142
1143 i = win32_kbd_patch_key(pker);
1144
1145 if (i < 0)
1146 *pch = NUL;
1147 else
1148 {
K.Takata972db232022-02-04 10:45:38 +00001149 *pch = (i > 0) ? pker->uChar.UnicodeChar : NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150
1151 if (pmodifiers != NULL)
1152 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001153 // Pass on the ALT key as a modifier, but only when not combined
1154 // with CTRL (which is ALTGR).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001155 if ((nModifs & ALT) != 0 && (nModifs & CTRL) == 0)
1156 *pmodifiers |= MOD_MASK_ALT;
1157
Bram Moolenaar0f873732019-12-05 20:28:46 +01001158 // Pass on SHIFT only for special keys, because we don't know when
1159 // it's already included with the character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001160 if ((nModifs & SHIFT) != 0 && *pch <= 0x20)
1161 *pmodifiers |= MOD_MASK_SHIFT;
1162
Bram Moolenaar0f873732019-12-05 20:28:46 +01001163 // Pass on CTRL only for non-special keys, because we don't know
1164 // when it's already included with the character. And not when
1165 // combined with ALT (which is ALTGR).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166 if ((nModifs & CTRL) != 0 && (nModifs & ALT) == 0
1167 && *pch >= 0x20 && *pch < 0x80)
1168 *pmodifiers |= MOD_MASK_CTRL;
1169 }
1170 }
1171
1172 return (*pch != NUL);
1173}
1174
Christopher Plewright20b795e2022-12-20 20:01:58 +00001175# if defined(FEAT_EVAL)
1176 static int
1177encode_key_event(dict_T *args, INPUT_RECORD *ir)
1178{
1179 static int s_dwMods = 0;
1180
1181 char_u *event = dict_get_string(args, "event", TRUE);
1182 if (event && (STRICMP(event, "keydown") == 0
1183 || STRICMP(event, "keyup") == 0))
1184 {
1185 WORD vkCode = dict_get_number_def(args, "keycode", 0);
1186 if (vkCode <= 0 || vkCode >= 0xFF)
1187 {
1188 semsg(_(e_invalid_argument_nr), (long)vkCode);
1189 return FALSE;
1190 }
1191
1192 ir->EventType = KEY_EVENT;
1193 KEY_EVENT_RECORD ker;
1194 ZeroMemory(&ker, sizeof(ker));
1195 ker.bKeyDown = STRICMP(event, "keydown") == 0;
1196 ker.wRepeatCount = 1;
1197 ker.wVirtualScanCode = 0;
1198 ker.dwControlKeyState = 0;
1199 int mods = (int)dict_get_number(args, "modifiers");
1200 // Encode the win32 console key modifiers from Vim keyboard modifiers.
1201 if (mods)
1202 {
1203 // If "modifiers" is explicitly set in the args, then we reset any
1204 // remembered modifer key state that may have been set from earlier
1205 // mod-key-down events, even if they are not yet unset by earlier
1206 // mod-key-up events.
1207 s_dwMods = 0;
1208 if (mods & MOD_MASK_SHIFT)
1209 ker.dwControlKeyState |= SHIFT_PRESSED;
1210 if (mods & MOD_MASK_CTRL)
1211 ker.dwControlKeyState |= LEFT_CTRL_PRESSED;
1212 if (mods & MOD_MASK_ALT)
1213 ker.dwControlKeyState |= LEFT_ALT_PRESSED;
1214 }
1215
1216 if (vkCode == VK_LSHIFT || vkCode == VK_RSHIFT || vkCode == VK_SHIFT)
1217 {
1218 if (STRICMP(event, "keydown") == 0)
1219 s_dwMods |= SHIFT_PRESSED;
1220 else
1221 s_dwMods &= ~SHIFT_PRESSED;
1222 }
1223 else if (vkCode == VK_LCONTROL || vkCode == VK_CONTROL)
1224 {
1225 if (STRICMP(event, "keydown") == 0)
1226 s_dwMods |= LEFT_CTRL_PRESSED;
1227 else
1228 s_dwMods &= ~LEFT_CTRL_PRESSED;
1229 }
1230 else if (vkCode == VK_RCONTROL)
1231 {
1232 if (STRICMP(event, "keydown") == 0)
1233 s_dwMods |= RIGHT_CTRL_PRESSED;
1234 else
1235 s_dwMods &= ~RIGHT_CTRL_PRESSED;
1236 }
1237 else if (vkCode == VK_LMENU || vkCode == VK_MENU)
1238 {
1239 if (STRICMP(event, "keydown") == 0)
1240 s_dwMods |= LEFT_ALT_PRESSED;
1241 else
1242 s_dwMods &= ~LEFT_ALT_PRESSED;
1243 }
1244 else if (vkCode == VK_RMENU)
1245 {
1246 if (STRICMP(event, "keydown") == 0)
1247 s_dwMods |= RIGHT_ALT_PRESSED;
1248 else
1249 s_dwMods &= ~RIGHT_ALT_PRESSED;
1250 }
1251 ker.dwControlKeyState |= s_dwMods;
1252 ker.wVirtualKeyCode = vkCode;
1253 win32_kbd_patch_key(&ker);
1254
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001255 for (int i = ARRAY_LENGTH(VirtKeyMap); i >= 0; --i)
Christopher Plewright20b795e2022-12-20 20:01:58 +00001256 {
1257 if (VirtKeyMap[i].wVirtKey == vkCode)
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001258 {
Christopher Plewright20b795e2022-12-20 20:01:58 +00001259 ker.uChar.UnicodeChar = 0xfffd; // REPLACEMENT CHARACTER
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001260 break;
1261 }
Christopher Plewright20b795e2022-12-20 20:01:58 +00001262 }
1263
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001264 // The following are treated specially in Vim.
1265 // Ctrl-6 is Ctrl-^
1266 // Ctrl-2 is Ctrl-@
1267 // Ctrl-- is Ctrl-_
1268 if ((vkCode == 0xBD || vkCode == '2' || vkCode == '6')
1269 && (ker.dwControlKeyState & CTRL))
1270 ker.uChar.UnicodeChar = 0xfffd; // REPLACEMENT CHARACTER
1271
Christopher Plewright20b795e2022-12-20 20:01:58 +00001272 ir->Event.KeyEvent = ker;
1273 vim_free(event);
1274 }
1275 else
1276 {
1277 if (event == NULL)
1278 {
1279 semsg(_(e_missing_argument_str), "event");
1280 }
1281 else
1282 {
1283 semsg(_(e_invalid_value_for_argument_str_str), "event", event);
1284 vim_free(event);
1285 }
1286 return FALSE;
1287 }
1288 return TRUE;
1289}
1290# endif // FEAT_EVAL
1291#endif // !FEAT_GUI_MSWIN || VIMDLL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292
1293
Bram Moolenaar071d4272004-06-13 20:20:40 +00001294/*
1295 * For the GUI the mouse handling is in gui_w32.c.
1296 */
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001297#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01001299mch_setmouse(int on UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300{
1301}
Christopher Plewright20b795e2022-12-20 20:01:58 +00001302#else // !FEAT_GUI_MSWIN || VIMDLL
Bram Moolenaar0f873732019-12-05 20:28:46 +01001303static int g_fMouseAvail = FALSE; // mouse present
1304static int g_fMouseActive = FALSE; // mouse enabled
1305static int g_nMouseClick = -1; // mouse status
1306static int g_xMouse; // mouse x coordinate
1307static int g_yMouse; // mouse y coordinate
Bram Moolenaar9f1983d2022-05-12 20:35:35 +01001308static DWORD g_cmodein = 0; // Original console input mode
1309static DWORD g_cmodeout = 0; // Original console output mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310
1311/*
1312 * Enable or disable mouse input
1313 */
1314 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001315mch_setmouse(int on)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316{
1317 DWORD cmodein;
1318
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001319# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001320 if (gui.in_use)
1321 return;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001322# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 if (!g_fMouseAvail)
1324 return;
1325
1326 g_fMouseActive = on;
1327 GetConsoleMode(g_hConIn, &cmodein);
1328
1329 if (g_fMouseActive)
Wez Furlong6ef5ab52021-05-30 19:29:41 +02001330 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 cmodein |= ENABLE_MOUSE_INPUT;
Wez Furlong6ef5ab52021-05-30 19:29:41 +02001332 cmodein &= ~ENABLE_QUICK_EDIT_MODE;
1333 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001334 else
Wez Furlong6ef5ab52021-05-30 19:29:41 +02001335 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001336 cmodein &= ~ENABLE_MOUSE_INPUT;
Wez Furlong6ef5ab52021-05-30 19:29:41 +02001337 cmodein |= g_cmodein & ENABLE_QUICK_EDIT_MODE;
1338 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339
Wez Furlong6ef5ab52021-05-30 19:29:41 +02001340 SetConsoleMode(g_hConIn, cmodein | ENABLE_EXTENDED_FLAGS);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341}
1342
Bram Moolenaar157d8132018-03-06 17:09:20 +01001343
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001344# if defined(FEAT_BEVAL_TERM) || defined(PROTO)
Bram Moolenaar157d8132018-03-06 17:09:20 +01001345/*
1346 * Called when 'balloonevalterm' changed.
1347 */
1348 void
1349mch_bevalterm_changed(void)
1350{
1351 mch_setmouse(g_fMouseActive);
1352}
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001353# endif
Bram Moolenaar157d8132018-03-06 17:09:20 +01001354
Christopher Plewright2a46f812022-10-16 19:47:45 +01001355/*
1356 * Win32 console mouse scroll event handler.
Christopher Plewright20b795e2022-12-20 20:01:58 +00001357 * Console version of the _OnMouseWheel() function in gui_w32.c
Christopher Plewright2a46f812022-10-16 19:47:45 +01001358 *
1359 * This encodes the mouse scroll direction and keyboard modifiers into
1360 * g_nMouseClick, and the mouse position into g_xMouse and g_yMouse
1361 *
1362 * The direction of the scroll is decoded from two fields of the win32 console
1363 * mouse event record;
Christopher Plewright20b795e2022-12-20 20:01:58 +00001364 * 1. The orientation - vertical or horizontal flag - from dwEventFlags
Christopher Plewright2a46f812022-10-16 19:47:45 +01001365 * 2. The sign - positive or negative (aka delta flag) - from dwButtonState
1366 *
Christopher Plewright20b795e2022-12-20 20:01:58 +00001367 * When scroll orientation is HORIZONTAL
Christopher Plewright2a46f812022-10-16 19:47:45 +01001368 * - If the high word of the dwButtonState member contains a positive
1369 * value, the wheel was rotated to the right.
1370 * - Otherwise, the wheel was rotated to the left.
Christopher Plewright20b795e2022-12-20 20:01:58 +00001371 * When scroll orientation is VERTICAL
Christopher Plewright2a46f812022-10-16 19:47:45 +01001372 * - If the high word of the dwButtonState member contains a positive value,
1373 * the wheel was rotated forward, away from the user.
1374 * - Otherwise, the wheel was rotated backward, toward the user.
1375 */
1376 static void
1377decode_mouse_wheel(MOUSE_EVENT_RECORD *pmer)
1378{
Christopher Plewright2a46f812022-10-16 19:47:45 +01001379 int horizontal = (pmer->dwEventFlags == MOUSE_HWHEELED);
1380 int zDelta = pmer->dwButtonState;
1381
1382 g_xMouse = pmer->dwMousePosition.X;
1383 g_yMouse = pmer->dwMousePosition.Y;
1384
K.Takata161b6ac2022-11-14 15:31:07 +00001385# ifdef FEAT_PROP_POPUP
Christopher Plewright605d02a2022-10-19 11:54:46 +01001386 int lcol = g_xMouse;
1387 int lrow = g_yMouse;
Christopher Plewright38804d62022-11-09 23:55:52 +00001388 win_T *wp = mouse_find_win(&lrow, &lcol, FIND_POPUP);
Christopher Plewright2a46f812022-10-16 19:47:45 +01001389 if (wp != NULL && popup_is_popup(wp))
1390 {
1391 g_nMouseClick = -1;
1392 cmdarg_T cap;
1393 oparg_T oa;
1394 CLEAR_FIELD(cap);
1395 clear_oparg(&oa);
1396 cap.oap = &oa;
1397 if (horizontal)
1398 {
1399 cap.arg = zDelta < 0 ? MSCR_LEFT : MSCR_RIGHT;
1400 cap.cmdchar = zDelta < 0 ? K_MOUSELEFT : K_MOUSERIGHT;
1401 }
1402 else
1403 {
1404 cap.cmdchar = zDelta < 0 ? K_MOUSEUP : K_MOUSEDOWN;
1405 cap.arg = zDelta < 0 ? MSCR_UP : MSCR_DOWN;
1406 }
1407
1408 // Mouse hovers over popup window, scroll it if possible.
1409 mouse_row = wp->w_winrow;
1410 mouse_col = wp->w_wincol;
1411 nv_mousescroll(&cap);
1412 update_screen(0);
1413 setcursor();
1414 out_flush();
1415 return;
1416 }
K.Takata161b6ac2022-11-14 15:31:07 +00001417# endif
Christopher Plewright2a46f812022-10-16 19:47:45 +01001418 mouse_col = g_xMouse;
1419 mouse_row = g_yMouse;
1420
1421 char_u modifiers = 0;
1422 char_u direction = 0;
1423
1424 // Decode the direction into an event that Vim can process
1425 if (horizontal)
1426 direction = zDelta >= 0 ? KE_MOUSELEFT : KE_MOUSERIGHT;
1427 else
1428 direction = zDelta >= 0 ? KE_MOUSEDOWN : KE_MOUSEUP;
1429
dundargocc57b5bc2022-11-02 13:30:51 +00001430 // Decode the win32 console key modifiers into Vim mouse modifiers.
Christopher Plewright2a46f812022-10-16 19:47:45 +01001431 if (pmer->dwControlKeyState & SHIFT_PRESSED)
Christopher Plewright605d02a2022-10-19 11:54:46 +01001432 modifiers |= MOD_MASK_SHIFT; // MOUSE_SHIFT;
Christopher Plewright2a46f812022-10-16 19:47:45 +01001433 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
Christopher Plewright605d02a2022-10-19 11:54:46 +01001434 modifiers |= MOD_MASK_CTRL; // MOUSE_CTRL;
Christopher Plewright2a46f812022-10-16 19:47:45 +01001435 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1436 modifiers |= MOD_MASK_ALT; // MOUSE_ALT;
1437
1438 // add (bitwise or) the scroll direction and the key modifier chars
1439 // together.
1440 g_nMouseClick = ((direction << 8) | modifiers);
1441
1442 return;
1443}
1444
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445/*
1446 * Decode a MOUSE_EVENT. If it's a valid event, return MOUSE_LEFT,
1447 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
1448 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
1449 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT. We encode the button type,
1450 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
1451 * and we return the mouse position in g_xMouse and g_yMouse.
1452 *
1453 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
1454 * MOUSE_DRAGs and one MOUSE_RELEASE. MOUSE_RELEASE will be followed only
1455 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
1456 *
1457 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
1458 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
1459 *
1460 * Windows will send us MOUSE_MOVED notifications whenever the mouse
1461 * moves, even if it stays within the same character cell. We ignore
1462 * all MOUSE_MOVED messages if the position hasn't really changed, and
1463 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
1464 * we're only interested in MOUSE_DRAG).
1465 *
1466 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
1467 * 2-button mouses by pressing the left & right buttons simultaneously.
1468 * In practice, it's almost impossible to click both at the same time,
1469 * so we need to delay a little. Also, we tend not to get MOUSE_RELEASE
1470 * in such cases, if the user is clicking quickly.
1471 */
1472 static BOOL
1473decode_mouse_event(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00001474 MOUSE_EVENT_RECORD *pmer)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475{
1476 static int s_nOldButton = -1;
1477 static int s_nOldMouseClick = -1;
1478 static int s_xOldMouse = -1;
1479 static int s_yOldMouse = -1;
1480 static linenr_T s_old_topline = 0;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001481# ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00001482 static int s_old_topfill = 0;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001483# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 static int s_cClicks = 1;
1485 static BOOL s_fReleased = TRUE;
1486 static DWORD s_dwLastClickTime = 0;
1487 static BOOL s_fNextIsMiddle = FALSE;
1488
Bram Moolenaar0f873732019-12-05 20:28:46 +01001489 static DWORD cButtons = 0; // number of buttons supported
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490
1491 const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
1492 const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
1493 const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
1494 const DWORD LEFT_RIGHT = LEFT | RIGHT;
1495
1496 int nButton;
1497
1498 if (cButtons == 0 && !GetNumberOfConsoleMouseButtons(&cButtons))
1499 cButtons = 2;
1500
1501 if (!g_fMouseAvail || !g_fMouseActive)
1502 {
1503 g_nMouseClick = -1;
1504 return FALSE;
1505 }
1506
Bram Moolenaar0f873732019-12-05 20:28:46 +01001507 // get a spurious MOUSE_EVENT immediately after receiving focus; ignore
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 if (g_fJustGotFocus)
1509 {
1510 g_fJustGotFocus = FALSE;
1511 return FALSE;
1512 }
1513
Christopher Plewright605d02a2022-10-19 11:54:46 +01001514 // If there is an unprocessed mouse click drop this one.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515 if (g_nMouseClick != -1)
1516 return TRUE;
Christopher Plewright605d02a2022-10-19 11:54:46 +01001517
Christopher Plewright2a46f812022-10-16 19:47:45 +01001518 if (pmer->dwEventFlags == MOUSE_WHEELED
1519 || pmer->dwEventFlags == MOUSE_HWHEELED)
1520 {
1521 decode_mouse_wheel(pmer);
1522 return TRUE; // we now should have a mouse scroll in g_nMouseClick
1523 }
Christopher Plewright605d02a2022-10-19 11:54:46 +01001524
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 nButton = -1;
1526 g_xMouse = pmer->dwMousePosition.X;
1527 g_yMouse = pmer->dwMousePosition.Y;
1528
1529 if (pmer->dwEventFlags == MOUSE_MOVED)
1530 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001531 // Ignore MOUSE_MOVED events if (x, y) hasn't changed. (We get these
1532 // events even when the mouse moves only within a char cell.)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001533 if (s_xOldMouse == g_xMouse && s_yOldMouse == g_yMouse)
1534 return FALSE;
1535 }
1536
Bram Moolenaar0f873732019-12-05 20:28:46 +01001537 // If no buttons are pressed...
Bram Moolenaar071d4272004-06-13 20:20:40 +00001538 if ((pmer->dwButtonState & ((1 << cButtons) - 1)) == 0)
1539 {
Bram Moolenaar157d8132018-03-06 17:09:20 +01001540 nButton = MOUSE_RELEASE;
1541
Bram Moolenaar0f873732019-12-05 20:28:46 +01001542 // If the last thing returned was MOUSE_RELEASE, ignore this
Bram Moolenaar071d4272004-06-13 20:20:40 +00001543 if (s_fReleased)
Bram Moolenaar157d8132018-03-06 17:09:20 +01001544 {
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001545# ifdef FEAT_BEVAL_TERM
Bram Moolenaar0f873732019-12-05 20:28:46 +01001546 // do return mouse move events when we want them
Bram Moolenaar157d8132018-03-06 17:09:20 +01001547 if (p_bevalterm)
1548 nButton = MOUSE_DRAG;
1549 else
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001550# endif
Bram Moolenaar157d8132018-03-06 17:09:20 +01001551 return FALSE;
1552 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 s_fReleased = TRUE;
1555 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01001556 else // one or more buttons pressed
Bram Moolenaar071d4272004-06-13 20:20:40 +00001557 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001558 // on a 2-button mouse, hold down left and right buttons
1559 // simultaneously to get MIDDLE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001560
1561 if (cButtons == 2 && s_nOldButton != MOUSE_DRAG)
1562 {
1563 DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
1564
Bram Moolenaar0f873732019-12-05 20:28:46 +01001565 // if either left or right button only is pressed, see if the
1566 // next mouse event has both of them pressed
Bram Moolenaar071d4272004-06-13 20:20:40 +00001567 if (dwLR == LEFT || dwLR == RIGHT)
1568 {
1569 for (;;)
1570 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001571 // wait a short time for next input event
Bram Moolenaar071d4272004-06-13 20:20:40 +00001572 if (WaitForSingleObject(g_hConIn, p_mouset / 3)
1573 != WAIT_OBJECT_0)
1574 break;
1575 else
1576 {
1577 DWORD cRecords = 0;
1578 INPUT_RECORD ir;
1579 MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
1580
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001581 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582
1583 if (cRecords == 0 || ir.EventType != MOUSE_EVENT
1584 || !(pmer2->dwButtonState & LEFT_RIGHT))
1585 break;
1586 else
1587 {
1588 if (pmer2->dwEventFlags != MOUSE_MOVED)
1589 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001590 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591
1592 return decode_mouse_event(pmer2);
1593 }
1594 else if (s_xOldMouse == pmer2->dwMousePosition.X &&
1595 s_yOldMouse == pmer2->dwMousePosition.Y)
1596 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001597 // throw away spurious mouse move
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001598 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599
Bram Moolenaar0f873732019-12-05 20:28:46 +01001600 // are there any more mouse events in queue?
Bram Moolenaar3a69e112014-01-10 13:51:42 +01001601 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001602
1603 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
1604 break;
1605 }
1606 else
1607 break;
1608 }
1609 }
1610 }
1611 }
1612 }
1613
1614 if (s_fNextIsMiddle)
1615 {
1616 nButton = (pmer->dwEventFlags == MOUSE_MOVED)
1617 ? MOUSE_DRAG : MOUSE_MIDDLE;
1618 s_fNextIsMiddle = FALSE;
1619 }
1620 else if (cButtons == 2 &&
1621 ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
1622 {
1623 nButton = MOUSE_MIDDLE;
1624
1625 if (! s_fReleased && pmer->dwEventFlags != MOUSE_MOVED)
1626 {
1627 s_fNextIsMiddle = TRUE;
1628 nButton = MOUSE_RELEASE;
1629 }
1630 }
1631 else if ((pmer->dwButtonState & LEFT) == LEFT)
1632 nButton = MOUSE_LEFT;
1633 else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
1634 nButton = MOUSE_MIDDLE;
1635 else if ((pmer->dwButtonState & RIGHT) == RIGHT)
1636 nButton = MOUSE_RIGHT;
1637
1638 if (! s_fReleased && ! s_fNextIsMiddle
1639 && nButton != s_nOldButton && s_nOldButton != MOUSE_DRAG)
1640 return FALSE;
1641
1642 s_fReleased = s_fNextIsMiddle;
1643 }
1644
1645 if (pmer->dwEventFlags == 0 || pmer->dwEventFlags == DOUBLE_CLICK)
1646 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01001647 // button pressed or released, without mouse moving
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648 if (nButton != -1 && nButton != MOUSE_RELEASE)
1649 {
1650 DWORD dwCurrentTime = GetTickCount();
1651
1652 if (s_xOldMouse != g_xMouse
1653 || s_yOldMouse != g_yMouse
1654 || s_nOldButton != nButton
1655 || s_old_topline != curwin->w_topline
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001656# ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00001657 || s_old_topfill != curwin->w_topfill
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001658# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001659 || (int)(dwCurrentTime - s_dwLastClickTime) > p_mouset)
1660 {
1661 s_cClicks = 1;
1662 }
1663 else if (++s_cClicks > 4)
1664 {
1665 s_cClicks = 1;
1666 }
1667
1668 s_dwLastClickTime = dwCurrentTime;
1669 }
1670 }
1671 else if (pmer->dwEventFlags == MOUSE_MOVED)
1672 {
1673 if (nButton != -1 && nButton != MOUSE_RELEASE)
1674 nButton = MOUSE_DRAG;
1675
1676 s_cClicks = 1;
1677 }
1678
1679 if (nButton == -1)
1680 return FALSE;
1681
1682 if (nButton != MOUSE_RELEASE)
1683 s_nOldButton = nButton;
1684
1685 g_nMouseClick = nButton;
1686
1687 if (pmer->dwControlKeyState & SHIFT_PRESSED)
1688 g_nMouseClick |= MOUSE_SHIFT;
1689 if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
1690 g_nMouseClick |= MOUSE_CTRL;
1691 if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
1692 g_nMouseClick |= MOUSE_ALT;
1693
1694 if (nButton != MOUSE_DRAG && nButton != MOUSE_RELEASE)
1695 SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
1696
Bram Moolenaar0f873732019-12-05 20:28:46 +01001697 // only pass on interesting (i.e., different) mouse events
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 if (s_xOldMouse == g_xMouse
1699 && s_yOldMouse == g_yMouse
1700 && s_nOldMouseClick == g_nMouseClick)
1701 {
1702 g_nMouseClick = -1;
1703 return FALSE;
1704 }
1705
1706 s_xOldMouse = g_xMouse;
1707 s_yOldMouse = g_yMouse;
1708 s_old_topline = curwin->w_topline;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001709# ifdef FEAT_DIFF
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710 s_old_topfill = curwin->w_topfill;
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02001711# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712 s_nOldMouseClick = g_nMouseClick;
1713
1714 return TRUE;
1715}
1716
Christopher Plewright20b795e2022-12-20 20:01:58 +00001717# ifdef FEAT_EVAL
1718 static int
1719encode_mouse_event(dict_T *args, INPUT_RECORD *ir)
1720{
1721 int button;
1722 int row;
1723 int col;
1724 int repeated_click;
Bram Moolenaar9b8a3652022-12-20 20:47:28 +00001725 int_u mods = 0;
Christopher Plewright20b795e2022-12-20 20:01:58 +00001726 int move;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001727
Christopher Plewright20b795e2022-12-20 20:01:58 +00001728 if (!dict_has_key(args, "row") || !dict_has_key(args, "col"))
1729 return FALSE;
1730
1731 // Note: "move" is optional, requires fewer arguments
1732 move = (int)dict_get_bool(args, "move", FALSE);
1733 if (!move && (!dict_has_key(args, "button")
1734 || !dict_has_key(args, "multiclick")
1735 || !dict_has_key(args, "modifiers")))
1736 return FALSE;
1737
1738 row = (int)dict_get_number(args, "row") - 1;
1739 col = (int)dict_get_number(args, "col") - 1;
1740
1741 ir->EventType = MOUSE_EVENT;
1742 MOUSE_EVENT_RECORD mer;
1743 ZeroMemory(&mer, sizeof(mer));
1744 mer.dwMousePosition.X = col;
1745 mer.dwMousePosition.Y = row;
1746
1747 if (move)
1748 {
1749 mer.dwButtonState = 0;
1750 mer.dwEventFlags = MOUSE_MOVED;
1751 }
1752 else
1753 {
1754 button = (int)dict_get_number(args, "button");
1755 repeated_click = (int)dict_get_number(args, "multiclick");
1756 mods = (int)dict_get_number(args, "modifiers");
1757 // Reset the scroll values to known values.
1758 // XXX: Remove this when/if the scroll step is made configurable.
1759 mouse_set_hor_scroll_step(6);
1760 mouse_set_vert_scroll_step(3);
1761
1762 switch (button)
1763 {
1764 case MOUSE_LEFT:
1765 mer.dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
1766 mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
1767 break;
1768 case MOUSE_MIDDLE:
1769 mer.dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
1770 mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
1771 break;
1772 case MOUSE_RIGHT:
1773 mer.dwButtonState = RIGHTMOST_BUTTON_PRESSED;
1774 mer.dwEventFlags = repeated_click == 1 ? DOUBLE_CLICK : 0;
1775 break;
1776 case MOUSE_RELEASE:
1777 // umm? Assume Left Release?
1778 mer.dwEventFlags = 0;
1779
1780 case MOUSE_MOVE:
1781 mer.dwButtonState = 0;
1782 mer.dwEventFlags = MOUSE_MOVED;
1783 break;
1784 case MOUSE_X1:
1785 mer.dwButtonState = FROM_LEFT_3RD_BUTTON_PRESSED;
1786 break;
1787 case MOUSE_X2:
1788 mer.dwButtonState = FROM_LEFT_4TH_BUTTON_PRESSED;
1789 break;
1790 case MOUSE_4: // KE_MOUSEDOWN;
1791 mer.dwButtonState = -1;
1792 mer.dwEventFlags = MOUSE_WHEELED;
1793 break;
1794 case MOUSE_5: // KE_MOUSEUP;
1795 mer.dwButtonState = +1;
1796 mer.dwEventFlags = MOUSE_WHEELED;
1797 break;
1798 case MOUSE_6: // KE_MOUSELEFT;
1799 mer.dwButtonState = -1;
1800 mer.dwEventFlags = MOUSE_HWHEELED;
1801 break;
1802 case MOUSE_7: // KE_MOUSERIGHT;
1803 mer.dwButtonState = +1;
1804 mer.dwEventFlags = MOUSE_HWHEELED;
1805 break;
1806 default:
1807 semsg(_(e_invalid_argument_str), "button");
1808 return FALSE;
1809 }
1810 }
1811
1812 mer.dwControlKeyState = 0;
1813 if (mods != 0)
1814 {
1815 // Encode the win32 console key modifiers from Vim MOUSE modifiers.
1816 if (mods & MOUSE_SHIFT)
1817 mer.dwControlKeyState |= SHIFT_PRESSED;
1818 if (mods & MOUSE_CTRL)
1819 mer.dwControlKeyState |= LEFT_CTRL_PRESSED;
1820 if (mods & MOUSE_ALT)
1821 mer.dwControlKeyState |= LEFT_ALT_PRESSED;
1822 }
1823 ir->Event.MouseEvent = mer;
1824 return TRUE;
1825}
1826# endif // FEAT_EVAL
1827
1828 static int
1829write_input_record_buffer(INPUT_RECORD* irEvents, int nLength)
1830{
1831 int nCount = 0;
1832 while (nCount < nLength)
1833 {
1834 input_record_buffer.length++;
1835 input_record_buffer_node_T *event_node =
1836 malloc(sizeof(input_record_buffer_node_T));
1837 event_node->ir = irEvents[nCount++];
1838 event_node->next = NULL;
1839 if (input_record_buffer.tail == NULL)
1840 {
1841 input_record_buffer.head = event_node;
1842 input_record_buffer.tail = event_node;
1843 }
1844 else
1845 {
1846 input_record_buffer.tail->next = event_node;
1847 input_record_buffer.tail = input_record_buffer.tail->next;
1848 }
1849 }
1850 return nCount;
1851}
1852
1853 static int
1854read_input_record_buffer(INPUT_RECORD* irEvents, int nMaxLength)
1855{
1856 int nCount = 0;
1857 while (nCount < nMaxLength && input_record_buffer.head != NULL)
1858 {
1859 input_record_buffer.length--;
1860 input_record_buffer_node_T *pop_head = input_record_buffer.head;
1861 irEvents[nCount++] = pop_head->ir;
1862 input_record_buffer.head = pop_head->next;
1863 vim_free(pop_head);
1864 if (input_record_buffer.length == 0)
1865 input_record_buffer.tail = NULL;
1866 }
1867 return nCount;
1868}
Christopher Plewright20b795e2022-12-20 20:01:58 +00001869#endif // !FEAT_GUI_MSWIN || VIMDLL
1870
1871#ifdef FEAT_EVAL
1872/*
1873 * The 'test_mswin_event' function is for testing Vim's low-level handling of
1874 * user input events. ie, this manages the encoding of INPUT_RECORD events
1875 * so that we have a way to test how Vim decodes INPUT_RECORD events in Windows
1876 * consoles.
1877 *
1878 * The 'test_mswin_event' function is based on 'test_gui_event'. In fact, when
1879 * the Windows GUI is running, the arguments; 'event' and 'args', are the same.
1880 * So, it acts as an alias for 'test_gui_event' for the Windows GUI.
1881 *
1882 * When the Windows console is running, the arguments; 'event' and 'args', are
1883 * a subset of what 'test_gui_event' handles, ie, only "key" and "mouse"
1884 * events are encoded as INPUT_RECORD events.
1885 *
1886 * Note: INPUT_RECORDs are only used by the Windows console, not the GUI. The
1887 * GUI sends MSG structs instead.
1888 */
1889 int
1890test_mswin_event(char_u *event, dict_T *args)
1891{
1892 int lpEventsWritten = 0;
1893
1894# if defined(VIMDLL) || defined(FEAT_GUI_MSWIN)
1895 if (gui.in_use)
1896 return test_gui_w32_sendevent(event, args);
1897# endif
1898
1899# if defined(VIMDLL) || !defined(FEAT_GUI_MSWIN)
1900
1901// Currently implemented event record types are; KEY_EVENT and MOUSE_EVENT
1902// Potentially could also implement: FOCUS_EVENT and WINDOW_BUFFER_SIZE_EVENT
1903// Maybe also: MENU_EVENT
1904
1905 INPUT_RECORD ir;
1906 BOOL input_encoded = FALSE;
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001907 BOOL execute = FALSE;
Christopher Plewright20b795e2022-12-20 20:01:58 +00001908 if (STRCMP(event, "key") == 0)
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001909 {
1910 execute = dict_get_bool(args, "execute", FALSE);
1911 if (dict_has_key(args, "event"))
1912 input_encoded = encode_key_event(args, &ir);
1913 else if (!execute)
1914 {
1915 semsg(_(e_missing_argument_str), "event");
1916 return FALSE;
1917 }
1918 }
Christopher Plewright20b795e2022-12-20 20:01:58 +00001919 else if (STRCMP(event, "mouse") == 0)
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001920 {
1921 execute = TRUE;
Christopher Plewright20b795e2022-12-20 20:01:58 +00001922 input_encoded = encode_mouse_event(args, &ir);
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001923 }
Christopher Plewright20b795e2022-12-20 20:01:58 +00001924 else
1925 {
1926 semsg(_(e_invalid_value_for_argument_str_str), "event", event);
1927 return FALSE;
1928 }
1929
1930 // Ideally, WriteConsoleInput would be used to inject these low-level
1931 // events. But, this doesnt work well in the CI test environment. So
1932 // implementing an input_record_buffer instead.
1933 if (input_encoded)
1934 lpEventsWritten = write_input_record_buffer(&ir, 1);
1935
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001936 // Set flags to execute the event, ie. like feedkeys mode X.
1937 if (execute)
1938 {
1939 int save_msg_scroll = msg_scroll;
1940 // Avoid a 1 second delay when the keys start Insert mode.
1941 msg_scroll = FALSE;
1942 ch_log(NULL, "test_mswin_event() executing");
Christopher Plewright20b795e2022-12-20 20:01:58 +00001943 exec_normal(TRUE, TRUE, TRUE);
Christopher Plewright7b0afc12022-12-30 16:54:58 +00001944 msg_scroll |= save_msg_scroll;
1945 }
Christopher Plewright20b795e2022-12-20 20:01:58 +00001946
1947# endif
1948 return lpEventsWritten;
1949}
1950#endif // FEAT_EVAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001951
1952#ifdef MCH_CURSOR_SHAPE
1953/*
1954 * Set the shape of the cursor.
1955 * 'thickness' can be from 1 (thin) to 99 (block)
1956 */
1957 static void
1958mch_set_cursor_shape(int thickness)
1959{
Christopher Plewright38804d62022-11-09 23:55:52 +00001960 if (vtp_working)
K.Takatadf5320c2022-09-01 13:20:16 +01001961 {
1962 if (*T_CSI == NUL)
1963 {
1964 // If 't_SI' is not set, use the default cursor styles.
1965 if (thickness < 50)
1966 vtp_printf("\033[3 q"); // underline
1967 else
1968 vtp_printf("\033[0 q"); // default
1969 }
1970 }
1971 else
1972 {
1973 CONSOLE_CURSOR_INFO ConsoleCursorInfo;
1974 ConsoleCursorInfo.dwSize = thickness;
1975 ConsoleCursorInfo.bVisible = s_cursor_visible;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976
K.Takatadf5320c2022-09-01 13:20:16 +01001977 SetConsoleCursorInfo(g_hConOut, &ConsoleCursorInfo);
1978 if (s_cursor_visible)
1979 SetConsoleCursorPosition(g_hConOut, g_coord);
1980 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001981}
1982
1983 void
1984mch_update_cursor(void)
1985{
1986 int idx;
1987 int thickness;
1988
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001989# ifdef VIMDLL
1990 if (gui.in_use)
1991 return;
1992# endif
1993
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 /*
1995 * How the cursor is drawn depends on the current mode.
1996 */
1997 idx = get_shape_idx(FALSE);
1998
1999 if (shape_table[idx].shape == SHAPE_BLOCK)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002000 thickness = 99; // 100 doesn't work on W95
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 else
2002 thickness = shape_table[idx].percentage;
2003 mch_set_cursor_shape(thickness);
2004}
2005#endif
2006
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002007#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008/*
2009 * Handle FOCUS_EVENT.
2010 */
2011 static void
2012handle_focus_event(INPUT_RECORD ir)
2013{
2014 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
2015 ui_focus_change((int)g_fJustGotFocus);
2016}
2017
Bram Moolenaar78d21da2019-02-17 15:00:52 +01002018static void ResizeConBuf(HANDLE hConsole, COORD coordScreen);
2019
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020/*
2021 * Wait until console input from keyboard or mouse is available,
2022 * or the time is up.
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002023 * When "ignore_input" is TRUE even wait when input is available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024 * Return TRUE if something is available FALSE if not.
2025 */
2026 static int
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002027WaitForChar(long msec, int ignore_input)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002028{
2029 DWORD dwNow = 0, dwEndTime = 0;
2030 INPUT_RECORD ir;
2031 DWORD cRecords;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002032 WCHAR ch, ch2;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002033# ifdef FEAT_TIMERS
Bram Moolenaar40b1b542016-04-20 20:18:23 +02002034 int tb_change_cnt = typebuf.tb_change_cnt;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002035# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036
2037 if (msec > 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002038 // Wait until the specified time has elapsed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002039 dwEndTime = GetTickCount() + msec;
2040 else if (msec < 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002041 // Wait forever.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002042 dwEndTime = INFINITE;
2043
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01002044 // We need to loop until the end of the time period, because
2045 // we might get multiple unusable mouse events in that time.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046 for (;;)
2047 {
Bram Moolenaared5a9d62018-09-06 13:14:43 +02002048 // Only process messages when waiting.
2049 if (msec != 0)
2050 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002051# ifdef MESSAGE_QUEUE
Bram Moolenaared5a9d62018-09-06 13:14:43 +02002052 parse_queued_messages();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002053# endif
2054# ifdef FEAT_MZSCHEME
Bram Moolenaared5a9d62018-09-06 13:14:43 +02002055 mzvim_check_threads();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002056# endif
2057# ifdef FEAT_CLIENTSERVER
Bram Moolenaared5a9d62018-09-06 13:14:43 +02002058 serverProcessPendingMessages();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002059# endif
Bram Moolenaared5a9d62018-09-06 13:14:43 +02002060 }
Bram Moolenaarf12d9832016-01-29 21:11:25 +01002061
Bram Moolenaara1cb1d12019-10-17 23:00:07 +02002062 if (g_nMouseClick != -1
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002063# ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002064 || (!ignore_input && input_available())
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002065# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066 )
2067 return TRUE;
2068
2069 if (msec > 0)
2070 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002071 // If the specified wait time has passed, return. Beware that
2072 // GetTickCount() may wrap around (overflow).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073 dwNow = GetTickCount();
Bram Moolenaarb7512b72013-08-10 12:45:09 +02002074 if ((int)(dwNow - dwEndTime) >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002075 break;
2076 }
2077 if (msec != 0)
2078 {
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002079 DWORD dwWaitTime = dwEndTime - dwNow;
2080
Bram Moolenaarc478ee32020-12-01 21:27:51 +01002081 // Don't wait for more than 11 msec to avoid dropping characters,
2082 // check channel while waiting for input and handle a callback from
2083 // 'balloonexpr'.
2084 if (dwWaitTime > 11)
2085 dwWaitTime = 11;
2086
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002087# ifdef FEAT_MZSCHEME
Bram Moolenaarc478ee32020-12-01 21:27:51 +01002088 if (mzthreads_allowed() && p_mzq > 0 && (long)dwWaitTime > p_mzq)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002089 dwWaitTime = p_mzq; // don't wait longer than 'mzquantum'
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002090# endif
2091# ifdef FEAT_TIMERS
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002092 // When waiting very briefly don't trigger timers.
2093 if (dwWaitTime > 10)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01002094 {
2095 long due_time;
2096
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002097 // Trigger timers and then get the time in msec until the next
2098 // one is due. Wait up to that time.
2099 due_time = check_due_timer();
2100 if (typebuf.tb_change_cnt != tb_change_cnt)
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01002101 {
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002102 // timer may have used feedkeys().
2103 return FALSE;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01002104 }
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002105 if (due_time > 0 && dwWaitTime > (DWORD)due_time)
2106 dwWaitTime = due_time;
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01002107 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002108# endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002109 if (
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002110# ifdef FEAT_CLIENTSERVER
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002111 // Wait for either an event on the console input or a
2112 // message in the client-server window.
2113 msg_wait_for_multiple_objects(1, &g_hConIn, FALSE,
2114 dwWaitTime, QS_SENDMESSAGE) != WAIT_OBJECT_0
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002115# else
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002116 wait_for_single_object(g_hConIn, dwWaitTime)
2117 != WAIT_OBJECT_0
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002118# endif
Bram Moolenaar1f20daa2019-01-19 21:12:24 +01002119 )
2120 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121 }
2122
2123 cRecords = 0;
Bram Moolenaar3a69e112014-01-10 13:51:42 +01002124 peek_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002126# ifdef FEAT_MBYTE_IME
matveyt4eb19142021-05-20 11:54:10 +02002127 // May have to redraw if the cursor ends up in the wrong place.
2128 // Only when not peeking.
Bram Moolenaar24959102022-05-07 20:01:16 +01002129 if (State == MODE_CMDLINE && msg_row == Rows - 1 && msec != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002130 {
2131 CONSOLE_SCREEN_BUFFER_INFO csbi;
2132
2133 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2134 {
2135 if (csbi.dwCursorPosition.Y != msg_row)
2136 {
matveyte08795e2021-05-07 15:00:17 +02002137 // The screen is now messed up, must redraw the command
2138 // line and later all the windows.
Bram Moolenaara4d158b2022-08-14 14:17:45 +01002139 redraw_all_later(UPD_CLEAR);
matveyte08795e2021-05-07 15:00:17 +02002140 compute_cmdrow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 redrawcmd();
2142 }
2143 }
2144 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002145# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146
2147 if (cRecords > 0)
2148 {
2149 if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
2150 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002151# ifdef FEAT_MBYTE_IME
Bram Moolenaar0f873732019-12-05 20:28:46 +01002152 // Windows IME sends two '\n's with only one 'ENTER'. First:
2153 // wVirtualKeyCode == 13. second: wVirtualKeyCode == 0
K.Takata972db232022-02-04 10:45:38 +00002154 if (ir.Event.KeyEvent.uChar.UnicodeChar == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155 && ir.Event.KeyEvent.wVirtualKeyCode == 13)
2156 {
Bram Moolenaar3a69e112014-01-10 13:51:42 +01002157 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158 continue;
2159 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002160# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002161 if (decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
2162 NULL, FALSE))
2163 return TRUE;
2164 }
2165
Bram Moolenaar3a69e112014-01-10 13:51:42 +01002166 read_console_input(g_hConIn, &ir, 1, &cRecords);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167
2168 if (ir.EventType == FOCUS_EVENT)
2169 handle_focus_event(ir);
2170 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
Bram Moolenaarc33ecb22018-02-11 16:40:45 +01002171 {
Bram Moolenaar78d21da2019-02-17 15:00:52 +01002172 COORD dwSize = ir.Event.WindowBufferSizeEvent.dwSize;
2173
Bram Moolenaar36698e32019-12-11 22:57:40 +01002174 // Only call shell_resized() when the size actually changed to
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002175 // avoid the screen is cleared.
Bram Moolenaar78d21da2019-02-17 15:00:52 +01002176 if (dwSize.X != Columns || dwSize.Y != Rows)
2177 {
2178 CONSOLE_SCREEN_BUFFER_INFO csbi;
2179 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
Bram Moolenaar36698e32019-12-11 22:57:40 +01002180 dwSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
Bram Moolenaar78d21da2019-02-17 15:00:52 +01002181 dwSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
Bram Moolenaar36698e32019-12-11 22:57:40 +01002182 if (dwSize.X != Columns || dwSize.Y != Rows)
2183 {
2184 ResizeConBuf(g_hConOut, dwSize);
2185 shell_resized();
2186 }
Bram Moolenaar78d21da2019-02-17 15:00:52 +01002187 }
Bram Moolenaarc33ecb22018-02-11 16:40:45 +01002188 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 else if (ir.EventType == MOUSE_EVENT
2190 && decode_mouse_event(&ir.Event.MouseEvent))
2191 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 }
2193 else if (msec == 0)
2194 break;
2195 }
2196
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002197# ifdef FEAT_CLIENTSERVER
Bram Moolenaar0f873732019-12-05 20:28:46 +01002198 // Something might have been received while we were waiting.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 if (input_available())
2200 return TRUE;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002201# endif
Bram Moolenaarf12d9832016-01-29 21:11:25 +01002202
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 return FALSE;
2204}
2205
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206/*
2207 * return non-zero if a character is available
2208 */
2209 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00002210mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002212# ifdef VIMDLL
2213 if (gui.in_use)
2214 return TRUE;
2215# endif
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002216 return WaitForChar(0L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217}
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002218
2219# if defined(FEAT_TERMINAL) || defined(PROTO)
2220/*
2221 * Check for any pending input or messages.
2222 */
2223 int
2224mch_check_messages(void)
2225{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002226# ifdef VIMDLL
2227 if (gui.in_use)
2228 return TRUE;
2229# endif
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002230 return WaitForChar(0L, TRUE);
2231}
2232# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233
2234/*
2235 * Create the console input. Used when reading stdin doesn't work.
2236 */
2237 static void
2238create_conin(void)
2239{
2240 g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
2241 FILE_SHARE_READ|FILE_SHARE_WRITE,
2242 (LPSECURITY_ATTRIBUTES) NULL,
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002243 OPEN_EXISTING, 0, (HANDLE)NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 did_create_conin = TRUE;
2245}
2246
2247/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01002248 * Get a keystroke or a mouse event, use a blocking wait.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002249 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002250 static WCHAR
2251tgetch(int *pmodifiers, WCHAR *pch2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002252{
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002253 WCHAR ch;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002254
2255 for (;;)
2256 {
2257 INPUT_RECORD ir;
2258 DWORD cRecords = 0;
2259
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002260# ifdef FEAT_CLIENTSERVER
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002261 (void)WaitForChar(-1L, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 if (input_available())
2263 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264 if (g_nMouseClick != -1)
2265 return 0;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002266# endif
Bram Moolenaar3a69e112014-01-10 13:51:42 +01002267 if (read_console_input(g_hConIn, &ir, 1, &cRecords) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268 {
2269 if (did_create_conin)
2270 read_error_exit();
2271 create_conin();
2272 continue;
2273 }
2274
2275 if (ir.EventType == KEY_EVENT)
2276 {
2277 if (decode_key_event(&ir.Event.KeyEvent, &ch, pch2,
2278 pmodifiers, TRUE))
2279 return ch;
2280 }
2281 else if (ir.EventType == FOCUS_EVENT)
2282 handle_focus_event(ir);
2283 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
2284 shell_resized();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285 else if (ir.EventType == MOUSE_EVENT)
2286 {
2287 if (decode_mouse_event(&ir.Event.MouseEvent))
2288 return 0;
2289 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002290 }
2291}
Bram Moolenaar0f873732019-12-05 20:28:46 +01002292#endif // !FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002293
2294
2295/*
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02002296 * mch_inchar(): low-level input function.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297 * Get one or more characters from the keyboard or the mouse.
2298 * If time == 0, do not wait for characters.
2299 * If time == n, wait a short time for characters.
2300 * If time == -1, wait forever for characters.
2301 * Returns the number of characters read into buf.
2302 */
2303 int
2304mch_inchar(
Bram Moolenaar1266d672017-02-01 13:43:36 +01002305 char_u *buf UNUSED,
2306 int maxlen UNUSED,
2307 long time UNUSED,
2308 int tb_change_cnt UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002309{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002310#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002311
2312 int len;
2313 int c;
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01002314# ifdef VIMDLL
2315// Extra space for maximum three CSIs. E.g. U+1B6DB -> 0xF0 0x9B 0x9B 0x9B.
2316# define TYPEAHEADSPACE 6
2317# else
2318# define TYPEAHEADSPACE 0
2319# endif
2320# define TYPEAHEADLEN (20 + TYPEAHEADSPACE)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002321 static char_u typeahead[TYPEAHEADLEN]; // previously typed bytes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322 static int typeaheadlen = 0;
2323
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002324# ifdef VIMDLL
2325 if (gui.in_use)
2326 return 0;
2327# endif
2328
Bram Moolenaar0f873732019-12-05 20:28:46 +01002329 // First use any typeahead that was kept because "buf" was too small.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002330 if (typeaheadlen > 0)
2331 goto theend;
2332
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333 if (time >= 0)
2334 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002335 if (!WaitForChar(time, FALSE)) // no character available
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01002338 else // time == -1, wait forever
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002340 mch_set_winsize_now(); // Allow winsize changes from now on
Bram Moolenaar071d4272004-06-13 20:20:40 +00002341
Bram Moolenaar3918c952005-03-15 22:34:55 +00002342 /*
2343 * If there is no character available within 2 seconds (default)
2344 * write the autoscript file to disk. Or cause the CursorHold event
2345 * to be triggered.
2346 */
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002347 if (!WaitForChar(p_ut, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348 {
Bram Moolenaard35f9712005-12-18 22:02:33 +00002349 if (trigger_cursorhold() && maxlen >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 {
Bram Moolenaar3918c952005-03-15 22:34:55 +00002351 buf[0] = K_SPECIAL;
2352 buf[1] = KS_EXTRA;
2353 buf[2] = (int)KE_CURSORHOLD;
2354 return 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 }
Bram Moolenaar702517d2005-06-27 22:34:07 +00002356 before_blocking();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002357 }
2358 }
2359
2360 /*
2361 * Try to read as many characters as there are, until the buffer is full.
2362 */
2363
Bram Moolenaar0f873732019-12-05 20:28:46 +01002364 // we will get at least one key. Get more if they are available.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 g_fCBrkPressed = FALSE;
2366
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002367# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002368 if (fdDump)
2369 fputc('[', fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002370# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371
Bram Moolenaar0f873732019-12-05 20:28:46 +01002372 // Keep looping until there is something in the typeahead buffer and more
2373 // to get and still room in the buffer (up to two bytes for a char and
2374 // three bytes for a modifier).
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02002375 while ((typeaheadlen == 0 || WaitForChar(0L, FALSE))
Bram Moolenaar9f1983d2022-05-12 20:35:35 +01002376 && typeaheadlen + 5 + TYPEAHEADSPACE <= TYPEAHEADLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 {
2378 if (typebuf_changed(tb_change_cnt))
2379 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002380 // "buf" may be invalid now if a client put something in the
2381 // typeahead buffer and "buf" is in the typeahead buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382 typeaheadlen = 0;
2383 break;
2384 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385 if (g_nMouseClick != -1)
2386 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002387# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002388 if (fdDump)
2389 fprintf(fdDump, "{%02x @ %d, %d}",
2390 g_nMouseClick, g_xMouse, g_yMouse);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002391# endif
Christopher Plewright2a46f812022-10-16 19:47:45 +01002392 char_u modifiers = ((char_u *)(&g_nMouseClick))[0];
2393 char_u scroll_dir = ((char_u *)(&g_nMouseClick))[1];
2394
2395 if (scroll_dir == KE_MOUSEDOWN
2396 || scroll_dir == KE_MOUSEUP
2397 || scroll_dir == KE_MOUSELEFT
2398 || scroll_dir == KE_MOUSERIGHT)
2399 {
2400 if (modifiers > 0)
2401 {
Christopher Plewright03193062022-11-22 12:58:27 +00002402 // use K_SPECIAL instead of CSI to make mappings work
2403 typeahead[typeaheadlen++] = K_SPECIAL;
Christopher Plewright2a46f812022-10-16 19:47:45 +01002404 typeahead[typeaheadlen++] = KS_MODIFIER;
2405 typeahead[typeaheadlen++] = modifiers;
2406 }
2407 typeahead[typeaheadlen++] = CSI;
2408 typeahead[typeaheadlen++] = KS_EXTRA;
2409 typeahead[typeaheadlen++] = scroll_dir;
Christopher Plewright2a46f812022-10-16 19:47:45 +01002410 }
2411 else
2412 {
2413 typeahead[typeaheadlen++] = ESC + 128;
2414 typeahead[typeaheadlen++] = 'M';
2415 typeahead[typeaheadlen++] = g_nMouseClick;
Christopher Plewright2a46f812022-10-16 19:47:45 +01002416 }
Christopher Plewright36446bb2022-11-23 22:28:08 +00002417
2418 // Pass the pointer coordinates of the mouse event in 2 bytes,
2419 // allowing for > 223 columns. Both for click and scroll events.
2420 // This is the same as what is used for the GUI.
2421 typeahead[typeaheadlen++] = (char_u)(g_xMouse / 128 + ' ' + 1);
2422 typeahead[typeaheadlen++] = (char_u)(g_xMouse % 128 + ' ' + 1);
2423 typeahead[typeaheadlen++] = (char_u)(g_yMouse / 128 + ' ' + 1);
2424 typeahead[typeaheadlen++] = (char_u)(g_yMouse % 128 + ' ' + 1);
2425
2426 g_nMouseClick = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 }
2428 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002430 WCHAR ch2 = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 int modifiers = 0;
2432
2433 c = tgetch(&modifiers, &ch2);
2434
Christopher Plewright7b0afc12022-12-30 16:54:58 +00002435 // Some chars need adjustment when the Ctrl modifier is used.
2436 ++no_reduce_keys;
2437 c = may_adjust_key_for_ctrl(modifiers, c);
2438 --no_reduce_keys;
2439
2440 // remove the SHIFT modifier for keys where it's already included,
2441 // e.g., '(' and '*'
2442 modifiers = may_remove_shift_modifier(modifiers, c);
2443
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 if (typebuf_changed(tb_change_cnt))
2445 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002446 // "buf" may be invalid now if a client put something in the
2447 // typeahead buffer and "buf" is in the typeahead buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 typeaheadlen = 0;
2449 break;
2450 }
2451
2452 if (c == Ctrl_C && ctrl_c_interrupts)
2453 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002454# if defined(FEAT_CLIENTSERVER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455 trash_input_buf();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002456# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 got_int = TRUE;
2458 }
2459
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460 if (g_nMouseClick == -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461 {
2462 int n = 1;
2463
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002464 if (ch2 == NUL)
2465 {
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01002466 int i, j;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002467 char_u *p;
2468 WCHAR ch[2];
2469
2470 ch[0] = c;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002471 if (c >= 0xD800 && c <= 0xDBFF) // High surrogate
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002472 {
2473 ch[1] = tgetch(&modifiers, &ch2);
2474 n++;
2475 }
2476 p = utf16_to_enc(ch, &n);
2477 if (p != NULL)
2478 {
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01002479 for (i = 0, j = 0; i < n; i++)
2480 {
2481 typeahead[typeaheadlen + j++] = p[i];
2482# ifdef VIMDLL
2483 if (p[i] == CSI)
2484 {
2485 typeahead[typeaheadlen + j++] = KS_EXTRA;
2486 typeahead[typeaheadlen + j++] = KE_CSI;
2487 }
2488# endif
2489 }
2490 n = j;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002491 vim_free(p);
2492 }
2493 }
2494 else
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01002495 {
Bram Moolenaarac360bf2015-09-01 20:31:20 +02002496 typeahead[typeaheadlen] = c;
Bram Moolenaar8f027fe2020-03-04 23:21:35 +01002497# ifdef VIMDLL
2498 if (c == CSI)
2499 {
2500 typeahead[typeaheadlen + 1] = KS_EXTRA;
2501 typeahead[typeaheadlen + 2] = KE_CSI;
2502 n = 3;
2503 }
2504# endif
2505 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506 if (ch2 != NUL)
2507 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02002508 if (c == K_NUL)
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01002509 {
Bram Moolenaar0cc7b2d2018-10-07 15:49:56 +02002510 switch (ch2)
2511 {
2512 case (WCHAR)'\324': // SHIFT+Insert
2513 case (WCHAR)'\325': // CTRL+Insert
2514 case (WCHAR)'\327': // SHIFT+Delete
2515 case (WCHAR)'\330': // CTRL+Delete
2516 typeahead[typeaheadlen + n] = (char_u)ch2;
2517 n++;
2518 break;
2519
2520 default:
2521 typeahead[typeaheadlen + n] = 3;
2522 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
2523 n += 2;
2524 break;
2525 }
Bram Moolenaarfeeb4d02017-12-05 15:14:46 +01002526 }
2527 else
2528 {
2529 typeahead[typeaheadlen + n] = 3;
2530 typeahead[typeaheadlen + n + 1] = (char_u)ch2;
2531 n += 2;
2532 }
Bram Moolenaar45500912014-07-09 20:51:07 +02002533 }
2534
Bram Moolenaar0f873732019-12-05 20:28:46 +01002535 // Use the ALT key to set the 8th bit of the character
2536 // when it's one byte, the 8th bit isn't set yet and not
2537 // using a double-byte encoding (would become a lead
2538 // byte).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539 if ((modifiers & MOD_MASK_ALT)
2540 && n == 1
2541 && (typeahead[typeaheadlen] & 0x80) == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002542 && !enc_dbcs
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543 )
2544 {
Bram Moolenaar85a3e5c2007-11-20 16:22:16 +00002545 n = (*mb_char2bytes)(typeahead[typeaheadlen] | 0x80,
2546 typeahead + typeaheadlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 modifiers &= ~MOD_MASK_ALT;
2548 }
2549
2550 if (modifiers != 0)
2551 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002552 // Prepend modifiers to the character.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553 mch_memmove(typeahead + typeaheadlen + 3,
2554 typeahead + typeaheadlen, n);
2555 typeahead[typeaheadlen++] = K_SPECIAL;
2556 typeahead[typeaheadlen++] = (char_u)KS_MODIFIER;
2557 typeahead[typeaheadlen++] = modifiers;
2558 }
2559
2560 typeaheadlen += n;
2561
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002562# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002563 if (fdDump)
2564 fputc(c, fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002565# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566 }
2567 }
2568 }
2569
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002570# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002571 if (fdDump)
2572 {
2573 fputs("]\n", fdDump);
2574 fflush(fdDump);
2575 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002576# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577
Bram Moolenaar071d4272004-06-13 20:20:40 +00002578theend:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002579 // Move typeahead to "buf", as much as fits.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002580 len = 0;
2581 while (len < maxlen && typeaheadlen > 0)
2582 {
2583 buf[len++] = typeahead[0];
2584 mch_memmove(typeahead, typeahead + 1, --typeaheadlen);
2585 }
Bram Moolenaar4c5678f2022-11-30 18:12:19 +00002586# ifdef FEAT_EVAL
Bram Moolenaar7ca86fe2020-09-03 19:25:11 +02002587 if (len > 0)
2588 {
2589 buf[len] = NUL;
2590 ch_log(NULL, "raw key input: \"%s\"", buf);
2591 }
K.Takata6e1d31e2022-02-03 13:05:32 +00002592# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593 return len;
2594
Bram Moolenaar0f873732019-12-05 20:28:46 +01002595#else // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596 return 0;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002597#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598}
2599
Bram Moolenaar82881492012-11-20 16:53:39 +01002600#ifndef PROTO
2601# ifndef __MINGW32__
Bram Moolenaar0f873732019-12-05 20:28:46 +01002602# include <shellapi.h> // required for FindExecutable()
Bram Moolenaar82881492012-11-20 16:53:39 +01002603# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604#endif
2605
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002606/*
Bram Moolenaar95da1362020-05-30 18:37:55 +02002607 * Return TRUE if "name" is an executable file, FALSE if not or it doesn't exist.
Bram Moolenaar43663192017-03-05 14:29:12 +01002608 * When returning TRUE and "path" is not NULL save the path and set "*path" to
2609 * the allocated memory.
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00002610 * TODO: Should somehow check if it's really executable.
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00002611 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612 static int
Bram Moolenaar95da1362020-05-30 18:37:55 +02002613executable_file(char *name, char_u **path)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614{
LemonBoy40fd7e62022-05-05 20:18:16 +01002615 int attrs = win32_getattrs((char_u *)name);
2616
2617 // The file doesn't exist or is a folder.
2618 if (attrs == -1 || (attrs & FILE_ATTRIBUTE_DIRECTORY))
2619 return FALSE;
2620 // Check if the file is an AppExecLink, a special alias used by Windows
2621 // Store for its apps.
2622 if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar43663192017-03-05 14:29:12 +01002623 {
LemonBoy40fd7e62022-05-05 20:18:16 +01002624 char_u *res = resolve_appexeclink((char_u *)name);
2625 if (res == NULL)
2626 return FALSE;
2627 // The path is already absolute.
Bram Moolenaar95da1362020-05-30 18:37:55 +02002628 if (path != NULL)
LemonBoy40fd7e62022-05-05 20:18:16 +01002629 *path = res;
2630 else
2631 vim_free(res);
Bram Moolenaar95da1362020-05-30 18:37:55 +02002632 }
LemonBoy40fd7e62022-05-05 20:18:16 +01002633 else if (path != NULL)
2634 *path = FullName_save((char_u *)name, FALSE);
2635 return TRUE;
Bram Moolenaar95da1362020-05-30 18:37:55 +02002636}
2637
2638/*
2639 * If "use_path" is TRUE: Return TRUE if "name" is in $PATH.
2640 * If "use_path" is FALSE: Return TRUE if "name" exists.
2641 * If "use_pathext" is TRUE search "name" with extensions in $PATHEXT.
2642 * When returning TRUE and "path" is not NULL save the path and set "*path" to
2643 * the allocated memory.
2644 */
2645 static int
2646executable_exists(char *name, char_u **path, int use_path, int use_pathext)
2647{
2648 // WinNT and later can use _MAX_PATH wide characters for a pathname, which
2649 // means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
2650 // UTF-8.
2651 char_u buf[_MAX_PATH * 3];
2652 size_t len = STRLEN(name);
2653 size_t tmplen;
2654 char_u *p, *e, *e2;
2655 char_u *pathbuf = NULL;
2656 char_u *pathext = NULL;
2657 char_u *pathextbuf = NULL;
Mike Williamsa3d1b292021-06-30 20:56:00 +02002658 char_u *shname = NULL;
Bram Moolenaar95da1362020-05-30 18:37:55 +02002659 int noext = FALSE;
2660 int retval = FALSE;
2661
2662 if (len >= sizeof(buf)) // safety check
Bram Moolenaar43663192017-03-05 14:29:12 +01002663 return FALSE;
Bram Moolenaar95da1362020-05-30 18:37:55 +02002664
2665 // Using the name directly when a Unix-shell like 'shell'.
Mike Williamsa3d1b292021-06-30 20:56:00 +02002666 shname = gettail(p_sh);
2667 if (strstr((char *)shname, "sh") != NULL &&
2668 !(strstr((char *)shname, "powershell") != NULL
2669 || strstr((char *)shname, "pwsh") != NULL))
Bram Moolenaar95da1362020-05-30 18:37:55 +02002670 noext = TRUE;
2671
2672 if (use_pathext)
2673 {
2674 pathext = mch_getenv("PATHEXT");
2675 if (pathext == NULL)
2676 pathext = (char_u *)".com;.exe;.bat;.cmd";
2677
2678 if (noext == FALSE)
2679 {
2680 /*
2681 * Loop over all extensions in $PATHEXT.
2682 * Check "name" ends with extension.
2683 */
2684 p = pathext;
2685 while (*p)
2686 {
2687 if (p[0] == ';'
2688 || (p[0] == '.' && (p[1] == NUL || p[1] == ';')))
2689 {
2690 // Skip empty or single ".".
2691 ++p;
2692 continue;
2693 }
2694 e = vim_strchr(p, ';');
2695 if (e == NULL)
2696 e = p + STRLEN(p);
2697 tmplen = e - p;
2698
2699 if (_strnicoll(name + len - tmplen, (char *)p, tmplen) == 0)
2700 {
2701 noext = TRUE;
2702 break;
2703 }
2704
2705 p = e;
2706 }
2707 }
Bram Moolenaar43663192017-03-05 14:29:12 +01002708 }
2709
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00002710 // Prepend single "." to pathext, it means no extension added.
Bram Moolenaar95da1362020-05-30 18:37:55 +02002711 if (pathext == NULL)
2712 pathext = (char_u *)".";
2713 else if (noext == TRUE)
2714 {
2715 if (pathextbuf == NULL)
2716 pathextbuf = alloc(STRLEN(pathext) + 3);
2717 if (pathextbuf == NULL)
2718 {
2719 retval = FALSE;
2720 goto theend;
2721 }
2722 STRCPY(pathextbuf, ".;");
2723 STRCAT(pathextbuf, pathext);
2724 pathext = pathextbuf;
2725 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02002726
Bram Moolenaar95da1362020-05-30 18:37:55 +02002727 // Use $PATH when "use_path" is TRUE and "name" is basename.
2728 if (use_path && gettail((char_u *)name) == (char_u *)name)
2729 {
2730 p = mch_getenv("PATH");
2731 if (p != NULL)
2732 {
2733 pathbuf = alloc(STRLEN(p) + 3);
2734 if (pathbuf == NULL)
2735 {
2736 retval = FALSE;
2737 goto theend;
2738 }
Yasuhiro Matsumoto05cf63e2022-05-03 11:02:28 +01002739
2740 if (mch_getenv("NoDefaultCurrentDirectoryInExePath") == NULL)
2741 STRCPY(pathbuf, ".;");
2742 else
2743 *pathbuf = NUL;
Bram Moolenaar95da1362020-05-30 18:37:55 +02002744 STRCAT(pathbuf, p);
2745 }
2746 }
2747
2748 /*
2749 * Walk through all entries in $PATH to check if "name" exists there and
2750 * is an executable file.
2751 */
2752 p = (pathbuf != NULL) ? pathbuf : (char_u *)".";
2753 while (*p)
2754 {
2755 if (*p == ';') // Skip empty entry
2756 {
2757 ++p;
2758 continue;
2759 }
2760 e = vim_strchr(p, ';');
2761 if (e == NULL)
2762 e = p + STRLEN(p);
2763
2764 if (e - p + len + 2 > sizeof(buf))
2765 {
2766 retval = FALSE;
2767 goto theend;
2768 }
2769 // A single "." that means current dir.
2770 if (e - p == 1 && *p == '.')
2771 STRCPY(buf, name);
2772 else
2773 {
2774 vim_strncpy(buf, p, e - p);
2775 add_pathsep(buf);
2776 STRCAT(buf, name);
2777 }
2778 tmplen = STRLEN(buf);
2779
2780 /*
2781 * Loop over all extensions in $PATHEXT.
2782 * Check "name" with extension added.
2783 */
2784 p = pathext;
2785 while (*p)
2786 {
2787 if (*p == ';')
2788 {
2789 // Skip empty entry
2790 ++p;
2791 continue;
2792 }
2793 e2 = vim_strchr(p, (int)';');
2794 if (e2 == NULL)
2795 e2 = p + STRLEN(p);
2796
2797 if (!(p[0] == '.' && (p[1] == NUL || p[1] == ';')))
2798 {
2799 // Not a single "." that means no extension is added.
2800 if (e2 - p + tmplen + 1 > sizeof(buf))
2801 {
2802 retval = FALSE;
2803 goto theend;
2804 }
2805 vim_strncpy(buf + tmplen, p, e2 - p);
2806 }
2807 if (executable_file((char *)buf, path))
2808 {
2809 retval = TRUE;
2810 goto theend;
2811 }
2812
2813 p = e2;
2814 }
2815
2816 p = e;
2817 }
2818
2819theend:
2820 free(pathextbuf);
2821 free(pathbuf);
2822 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823}
2824
Bram Moolenaar1eed5322019-02-26 17:03:54 +01002825#if (defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x800) || \
2826 (defined(_MSC_VER) && _MSC_VER >= 1400)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002827/*
2828 * Bad parameter handler.
2829 *
2830 * Certain MS CRT functions will intentionally crash when passed invalid
2831 * parameters to highlight possible security holes. Setting this function as
2832 * the bad parameter handler will prevent the crash.
2833 *
2834 * In debug builds the parameters contain CRT information that might help track
2835 * down the source of a problem, but in non-debug builds the arguments are all
2836 * NULL/0. Debug builds will also produce assert dialogs from the CRT, it is
2837 * worth allowing these to make debugging of issues easier.
2838 */
2839 static void
Yegappan Lakshmanana34b4462022-06-11 10:43:26 +01002840bad_param_handler(const wchar_t *expression UNUSED,
2841 const wchar_t *function UNUSED,
2842 const wchar_t *file UNUSED,
2843 unsigned int line UNUSED,
2844 uintptr_t pReserved UNUSED)
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002845{
2846}
2847
2848# define SET_INVALID_PARAM_HANDLER \
2849 ((void)_set_invalid_parameter_handler(bad_param_handler))
2850#else
2851# define SET_INVALID_PARAM_HANDLER
2852#endif
2853
Bram Moolenaar4f974752019-02-17 17:44:42 +01002854#ifdef FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002855
2856/*
2857 * GUI version of mch_init().
2858 */
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002859 static void
2860mch_init_g(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002861{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002862# ifndef __MINGW32__
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863 extern int _fmode;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002864# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865
Bram Moolenaar0f873732019-12-05 20:28:46 +01002866 // Silently handle invalid parameters to CRT functions
Bram Moolenaard32a99a2010-09-21 17:29:23 +02002867 SET_INVALID_PARAM_HANDLER;
2868
Bram Moolenaar0f873732019-12-05 20:28:46 +01002869 // Let critical errors result in a failure, not in a dialog box. Required
2870 // for the timestamp test to work on removed floppies.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 SetErrorMode(SEM_FAILCRITICALERRORS);
2872
Bram Moolenaar0f873732019-12-05 20:28:46 +01002873 _fmode = O_BINARY; // we do our own CR-LF translation
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874
Bram Moolenaar0f873732019-12-05 20:28:46 +01002875 // Specify window size. Is there a place to get the default from?
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876 Rows = 25;
2877 Columns = 80;
2878
Bram Moolenaar0f873732019-12-05 20:28:46 +01002879 // Look for 'vimrun'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002880 {
2881 char_u vimrun_location[_MAX_PATH + 4];
2882
Bram Moolenaar0f873732019-12-05 20:28:46 +01002883 // First try in same directory as gvim.exe
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 STRCPY(vimrun_location, exe_name);
2885 STRCPY(gettail(vimrun_location), "vimrun.exe");
2886 if (mch_getperm(vimrun_location) >= 0)
2887 {
2888 if (*skiptowhite(vimrun_location) != NUL)
2889 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002890 // Enclose path with white space in double quotes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002891 mch_memmove(vimrun_location + 1, vimrun_location,
2892 STRLEN(vimrun_location) + 1);
2893 *vimrun_location = '"';
2894 STRCPY(gettail(vimrun_location), "vimrun\" ");
2895 }
2896 else
2897 STRCPY(gettail(vimrun_location), "vimrun ");
2898
2899 vimrun_path = (char *)vim_strsave(vimrun_location);
2900 s_dont_use_vimrun = FALSE;
2901 }
Bram Moolenaar95da1362020-05-30 18:37:55 +02002902 else if (executable_exists("vimrun.exe", NULL, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903 s_dont_use_vimrun = FALSE;
2904
Bram Moolenaar0f873732019-12-05 20:28:46 +01002905 // Don't give the warning for a missing vimrun.exe right now, but only
2906 // when vimrun was supposed to be used. Don't bother people that do
2907 // not need vimrun.exe.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002908 if (s_dont_use_vimrun)
2909 need_vimrun_warning = TRUE;
2910 }
2911
2912 /*
2913 * If "finstr.exe" doesn't exist, use "grep -n" for 'grepprg'.
2914 * Otherwise the default "findstr /n" is used.
2915 */
Bram Moolenaar95da1362020-05-30 18:37:55 +02002916 if (!executable_exists("findstr.exe", NULL, TRUE, FALSE))
Bram Moolenaar75ab5902022-04-18 15:36:40 +01002917 set_option_value_give_err((char_u *)"grepprg",
2918 0, (char_u *)"grep -n", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002919
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002920# ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01002921 win_clip_init();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002922# endif
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01002923
2924 vtp_flag_init();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002925}
2926
2927
Bram Moolenaar0f873732019-12-05 20:28:46 +01002928#endif // FEAT_GUI_MSWIN
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002929
2930#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002931
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002932# define SRWIDTH(sr) ((sr).Right - (sr).Left + 1)
2933# define SRHEIGHT(sr) ((sr).Bottom - (sr).Top + 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934
2935/*
2936 * ClearConsoleBuffer()
2937 * Description:
2938 * Clears the entire contents of the console screen buffer, using the
2939 * specified attribute.
2940 * Returns:
2941 * TRUE on success
2942 */
2943 static BOOL
2944ClearConsoleBuffer(WORD wAttribute)
2945{
2946 CONSOLE_SCREEN_BUFFER_INFO csbi;
2947 COORD coord;
2948 DWORD NumCells, dummy;
2949
2950 if (!GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2951 return FALSE;
2952
2953 NumCells = csbi.dwSize.X * csbi.dwSize.Y;
2954 coord.X = 0;
2955 coord.Y = 0;
2956 if (!FillConsoleOutputCharacter(g_hConOut, ' ', NumCells,
2957 coord, &dummy))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 if (!FillConsoleOutputAttribute(g_hConOut, wAttribute, NumCells,
2960 coord, &dummy))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002961 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002962
2963 return TRUE;
2964}
2965
2966/*
2967 * FitConsoleWindow()
2968 * Description:
2969 * Checks if the console window will fit within given buffer dimensions.
2970 * Also, if requested, will shrink the window to fit.
2971 * Returns:
2972 * TRUE on success
2973 */
2974 static BOOL
2975FitConsoleWindow(
2976 COORD dwBufferSize,
2977 BOOL WantAdjust)
2978{
2979 CONSOLE_SCREEN_BUFFER_INFO csbi;
2980 COORD dwWindowSize;
2981 BOOL NeedAdjust = FALSE;
2982
2983 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
2984 {
2985 /*
2986 * A buffer resize will fail if the current console window does
2987 * not lie completely within that buffer. To avoid this, we might
2988 * have to move and possibly shrink the window.
2989 */
2990 if (csbi.srWindow.Right >= dwBufferSize.X)
2991 {
2992 dwWindowSize.X = SRWIDTH(csbi.srWindow);
2993 if (dwWindowSize.X > dwBufferSize.X)
2994 dwWindowSize.X = dwBufferSize.X;
2995 csbi.srWindow.Right = dwBufferSize.X - 1;
2996 csbi.srWindow.Left = dwBufferSize.X - dwWindowSize.X;
2997 NeedAdjust = TRUE;
2998 }
2999 if (csbi.srWindow.Bottom >= dwBufferSize.Y)
3000 {
3001 dwWindowSize.Y = SRHEIGHT(csbi.srWindow);
3002 if (dwWindowSize.Y > dwBufferSize.Y)
3003 dwWindowSize.Y = dwBufferSize.Y;
3004 csbi.srWindow.Bottom = dwBufferSize.Y - 1;
3005 csbi.srWindow.Top = dwBufferSize.Y - dwWindowSize.Y;
3006 NeedAdjust = TRUE;
3007 }
3008 if (NeedAdjust && WantAdjust)
3009 {
3010 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &csbi.srWindow))
3011 return FALSE;
3012 }
3013 return TRUE;
3014 }
3015
3016 return FALSE;
3017}
3018
3019typedef struct ConsoleBufferStruct
3020{
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003021 BOOL IsValid;
3022 CONSOLE_SCREEN_BUFFER_INFO Info;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003023 PCHAR_INFO Buffer;
3024 COORD BufferSize;
Bram Moolenaar444fda22017-08-11 20:37:00 +02003025 PSMALL_RECT Regions;
3026 int NumRegions;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003027} ConsoleBuffer;
3028
3029/*
3030 * SaveConsoleBuffer()
3031 * Description:
3032 * Saves important information about the console buffer, including the
3033 * actual buffer contents. The saved information is suitable for later
3034 * restoration by RestoreConsoleBuffer().
3035 * Returns:
3036 * TRUE if all information was saved; FALSE otherwise
3037 * If FALSE, still sets cb->IsValid if buffer characteristics were saved.
3038 */
3039 static BOOL
3040SaveConsoleBuffer(
3041 ConsoleBuffer *cb)
3042{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003043 DWORD NumCells;
3044 COORD BufferCoord;
3045 SMALL_RECT ReadRegion;
3046 WORD Y, Y_incr;
Bram Moolenaar444fda22017-08-11 20:37:00 +02003047 int i, numregions;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003048
Bram Moolenaar071d4272004-06-13 20:20:40 +00003049 if (cb == NULL)
3050 return FALSE;
3051
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003052 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 {
3054 cb->IsValid = FALSE;
3055 return FALSE;
3056 }
3057 cb->IsValid = TRUE;
3058
Christopher Plewright1140b512022-11-12 18:46:05 +00003059 // VTP uses alternate screen buffer.
3060 // No need to save buffer contents for restoration.
Christopher Plewrightc8b126d2022-12-22 13:45:23 +00003061 if (use_alternate_screen_buffer)
Christopher Plewright1140b512022-11-12 18:46:05 +00003062 return TRUE;
3063
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003064 /*
3065 * Allocate a buffer large enough to hold the entire console screen
3066 * buffer. If this ConsoleBuffer structure has already been initialized
3067 * with a buffer of the correct size, then just use that one.
3068 */
3069 if (!cb->IsValid || cb->Buffer == NULL ||
3070 cb->BufferSize.X != cb->Info.dwSize.X ||
3071 cb->BufferSize.Y != cb->Info.dwSize.Y)
3072 {
3073 cb->BufferSize.X = cb->Info.dwSize.X;
3074 cb->BufferSize.Y = cb->Info.dwSize.Y;
3075 NumCells = cb->BufferSize.X * cb->BufferSize.Y;
3076 vim_free(cb->Buffer);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003077 cb->Buffer = ALLOC_MULT(CHAR_INFO, NumCells);
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003078 if (cb->Buffer == NULL)
3079 return FALSE;
3080 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003081
3082 /*
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003083 * We will now copy the console screen buffer into our buffer.
3084 * ReadConsoleOutput() seems to be limited as far as how much you
3085 * can read at a time. Empirically, this number seems to be about
3086 * 12000 cells (rows * columns). Start at position (0, 0) and copy
3087 * in chunks until it is all copied. The chunks will all have the
3088 * same horizontal characteristics, so initialize them now. The
3089 * height of each chunk will be (12000 / width).
Bram Moolenaar071d4272004-06-13 20:20:40 +00003090 */
Bram Moolenaar61594242015-09-01 20:23:37 +02003091 BufferCoord.X = 0;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003092 ReadRegion.Left = 0;
3093 ReadRegion.Right = cb->Info.dwSize.X - 1;
3094 Y_incr = 12000 / cb->Info.dwSize.X;
Bram Moolenaar444fda22017-08-11 20:37:00 +02003095
3096 numregions = (cb->Info.dwSize.Y + Y_incr - 1) / Y_incr;
3097 if (cb->Regions == NULL || numregions != cb->NumRegions)
3098 {
3099 cb->NumRegions = numregions;
3100 vim_free(cb->Regions);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003101 cb->Regions = ALLOC_MULT(SMALL_RECT, cb->NumRegions);
Bram Moolenaar444fda22017-08-11 20:37:00 +02003102 if (cb->Regions == NULL)
3103 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01003104 VIM_CLEAR(cb->Buffer);
Bram Moolenaar444fda22017-08-11 20:37:00 +02003105 return FALSE;
3106 }
3107 }
3108
3109 for (i = 0, Y = 0; i < cb->NumRegions; i++, Y += Y_incr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003110 {
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003111 /*
3112 * Read into position (0, Y) in our buffer.
3113 */
3114 BufferCoord.Y = Y;
3115 /*
3116 * Read the region whose top left corner is (0, Y) and whose bottom
3117 * right corner is (width - 1, Y + Y_incr - 1). This should define
3118 * a region of size width by Y_incr. Don't worry if this region is
3119 * too large for the remaining buffer; it will be cropped.
3120 */
3121 ReadRegion.Top = Y;
3122 ReadRegion.Bottom = Y + Y_incr - 1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003123 if (!ReadConsoleOutputW(g_hConOut, // output handle
3124 cb->Buffer, // our buffer
3125 cb->BufferSize, // dimensions of our buffer
3126 BufferCoord, // offset in our buffer
3127 &ReadRegion)) // region to save
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003128 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01003129 VIM_CLEAR(cb->Buffer);
3130 VIM_CLEAR(cb->Regions);
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003131 return FALSE;
3132 }
Bram Moolenaar444fda22017-08-11 20:37:00 +02003133 cb->Regions[i] = ReadRegion;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003134 }
3135
3136 return TRUE;
3137}
3138
3139/*
3140 * RestoreConsoleBuffer()
3141 * Description:
3142 * Restores important information about the console buffer, including the
3143 * actual buffer contents, if desired. The information to restore is in
3144 * the same format used by SaveConsoleBuffer().
3145 * Returns:
3146 * TRUE on success
3147 */
3148 static BOOL
3149RestoreConsoleBuffer(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003150 ConsoleBuffer *cb,
3151 BOOL RestoreScreen)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152{
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003153 COORD BufferCoord;
3154 SMALL_RECT WriteRegion;
Bram Moolenaar444fda22017-08-11 20:37:00 +02003155 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156
Christopher Plewright1140b512022-11-12 18:46:05 +00003157 // VTP uses alternate screen buffer.
3158 // No need to restore buffer contents.
Christopher Plewrightc8b126d2022-12-22 13:45:23 +00003159 if (use_alternate_screen_buffer)
Christopher Plewright1140b512022-11-12 18:46:05 +00003160 return TRUE;
3161
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162 if (cb == NULL || !cb->IsValid)
3163 return FALSE;
3164
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003165 /*
3166 * Before restoring the buffer contents, clear the current buffer, and
3167 * restore the cursor position and window information. Doing this now
3168 * prevents old buffer contents from "flashing" onto the screen.
3169 */
3170 if (RestoreScreen)
3171 ClearConsoleBuffer(cb->Info.wAttributes);
3172
3173 FitConsoleWindow(cb->Info.dwSize, TRUE);
3174 if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
3175 return FALSE;
3176 if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
3177 return FALSE;
3178
3179 if (!RestoreScreen)
3180 {
3181 /*
3182 * No need to restore the screen buffer contents, so we're done.
3183 */
3184 return TRUE;
3185 }
3186
3187 if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
3188 return FALSE;
3189 if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
3190 return FALSE;
3191
3192 /*
3193 * Restore the screen buffer contents.
3194 */
3195 if (cb->Buffer != NULL)
3196 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02003197 for (i = 0; i < cb->NumRegions; i++)
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003198 {
Bram Moolenaar444fda22017-08-11 20:37:00 +02003199 BufferCoord.X = cb->Regions[i].Left;
3200 BufferCoord.Y = cb->Regions[i].Top;
3201 WriteRegion = cb->Regions[i];
Bram Moolenaar0f873732019-12-05 20:28:46 +01003202 if (!WriteConsoleOutputW(g_hConOut, // output handle
3203 cb->Buffer, // our buffer
3204 cb->BufferSize, // dimensions of our buffer
3205 BufferCoord, // offset in our buffer
3206 &WriteRegion)) // region to restore
Bram Moolenaar444fda22017-08-11 20:37:00 +02003207 return FALSE;
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003208 }
3209 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210
3211 return TRUE;
3212}
3213
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003214# define FEAT_RESTORE_ORIG_SCREEN
3215# ifdef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003216static ConsoleBuffer g_cbOrig = { 0 };
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003217# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218static ConsoleBuffer g_cbNonTermcap = { 0 };
3219static ConsoleBuffer g_cbTermcap = { 0 };
3220
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221char g_szOrigTitle[256] = { 0 };
Bram Moolenaar0f873732019-12-05 20:28:46 +01003222HWND g_hWnd = NULL; // also used in os_mswin.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223static HICON g_hOrigIconSmall = NULL;
3224static HICON g_hOrigIcon = NULL;
3225static HICON g_hVimIcon = NULL;
3226static BOOL g_fCanChangeIcon = FALSE;
3227
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228/*
3229 * GetConsoleIcon()
3230 * Description:
3231 * Attempts to retrieve the small icon and/or the big icon currently in
3232 * use by a given window.
3233 * Returns:
3234 * TRUE on success
3235 */
3236 static BOOL
3237GetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003238 HWND hWnd,
3239 HICON *phIconSmall,
3240 HICON *phIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241{
3242 if (hWnd == NULL)
3243 return FALSE;
3244
3245 if (phIconSmall != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003246 *phIconSmall = (HICON)SendMessage(hWnd, WM_GETICON,
3247 (WPARAM)ICON_SMALL, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248 if (phIcon != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003249 *phIcon = (HICON)SendMessage(hWnd, WM_GETICON,
3250 (WPARAM)ICON_BIG, (LPARAM)0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251 return TRUE;
3252}
3253
3254/*
3255 * SetConsoleIcon()
3256 * Description:
3257 * Attempts to change the small icon and/or the big icon currently in
3258 * use by a given window.
3259 * Returns:
3260 * TRUE on success
3261 */
3262 static BOOL
3263SetConsoleIcon(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003264 HWND hWnd,
3265 HICON hIconSmall,
3266 HICON hIcon)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003268 if (hWnd == NULL)
3269 return FALSE;
3270
3271 if (hIconSmall != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003272 SendMessage(hWnd, WM_SETICON,
3273 (WPARAM)ICON_SMALL, (LPARAM)hIconSmall);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274 if (hIcon != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003275 SendMessage(hWnd, WM_SETICON,
3276 (WPARAM)ICON_BIG, (LPARAM) hIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277 return TRUE;
3278}
3279
3280/*
3281 * SaveConsoleTitleAndIcon()
3282 * Description:
3283 * Saves the current console window title in g_szOrigTitle, for later
3284 * restoration. Also, attempts to obtain a handle to the console window,
3285 * and use it to save the small and big icons currently in use by the
3286 * console window. This is not always possible on some versions of Windows;
3287 * nor is it possible when running Vim remotely using Telnet (since the
3288 * console window the user sees is owned by a remote process).
3289 */
3290 static void
3291SaveConsoleTitleAndIcon(void)
3292{
Bram Moolenaar0f873732019-12-05 20:28:46 +01003293 // Save the original title.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003294 if (!GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle)))
3295 return;
3296
3297 /*
3298 * Obtain a handle to the console window using GetConsoleWindow() from
3299 * KERNEL32.DLL; we need to handle in order to change the window icon.
3300 * This function only exists on NT-based Windows, starting with Windows
3301 * 2000. On older operating systems, we can't change the window icon
3302 * anyway.
3303 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003304 g_hWnd = GetConsoleWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003305 if (g_hWnd == NULL)
3306 return;
3307
Bram Moolenaar0f873732019-12-05 20:28:46 +01003308 // Save the original console window icon.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003309 GetConsoleIcon(g_hWnd, &g_hOrigIconSmall, &g_hOrigIcon);
3310 if (g_hOrigIconSmall == NULL || g_hOrigIcon == NULL)
3311 return;
3312
Bram Moolenaar0f873732019-12-05 20:28:46 +01003313 // Extract the first icon contained in the Vim executable.
K.Takata2da11a42022-09-10 13:03:12 +01003314 if (
3315# ifdef FEAT_LIBCALL
3316 mch_icon_load((HANDLE *)&g_hVimIcon) == FAIL ||
3317# endif
3318 g_hVimIcon == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003319 g_hVimIcon = ExtractIcon(NULL, (LPCSTR)exe_name, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003320 if (g_hVimIcon != NULL)
3321 g_fCanChangeIcon = TRUE;
3322}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323
3324static int g_fWindInitCalled = FALSE;
3325static int g_fTermcapMode = FALSE;
3326static CONSOLE_CURSOR_INFO g_cci;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003327
3328/*
3329 * non-GUI version of mch_init().
3330 */
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003331 static void
3332mch_init_c(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003334# ifndef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003335 CONSOLE_SCREEN_BUFFER_INFO csbi;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003336# endif
3337# ifndef __MINGW32__
Bram Moolenaar071d4272004-06-13 20:20:40 +00003338 extern int _fmode;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003339# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340
Bram Moolenaar0f873732019-12-05 20:28:46 +01003341 // Silently handle invalid parameters to CRT functions
Bram Moolenaard32a99a2010-09-21 17:29:23 +02003342 SET_INVALID_PARAM_HANDLER;
3343
Bram Moolenaar0f873732019-12-05 20:28:46 +01003344 // Let critical errors result in a failure, not in a dialog box. Required
3345 // for the timestamp test to work on removed floppies.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 SetErrorMode(SEM_FAILCRITICALERRORS);
3347
Bram Moolenaar0f873732019-12-05 20:28:46 +01003348 _fmode = O_BINARY; // we do our own CR-LF translation
Bram Moolenaar071d4272004-06-13 20:20:40 +00003349 out_flush();
3350
Bram Moolenaar0f873732019-12-05 20:28:46 +01003351 // Obtain handles for the standard Console I/O devices
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 if (read_cmd_fd == 0)
3353 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
3354 else
3355 create_conin();
3356 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
3357
Christopher Plewright38804d62022-11-09 23:55:52 +00003358 wt_init();
3359 vtp_flag_init();
3360 vtp_init();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003361# ifdef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar0f873732019-12-05 20:28:46 +01003362 // Save the initial console buffer for later restoration
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003363 SaveConsoleBuffer(&g_cbOrig);
3364 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003365# else
Bram Moolenaar0f873732019-12-05 20:28:46 +01003366 // Get current text attributes
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01003367 GetConsoleScreenBufferInfo(g_hConOut, &csbi);
3368 g_attrCurrent = g_attrDefault = csbi.wAttributes;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003369# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 if (cterm_normal_fg_color == 0)
3371 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
3372 if (cterm_normal_bg_color == 0)
3373 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
3374
Bram Moolenaarbdace832019-03-02 10:13:42 +01003375 // Fg and Bg color index number at startup
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02003376 g_color_index_fg = g_attrDefault & 0xf;
3377 g_color_index_bg = (g_attrDefault >> 4) & 0xf;
3378
Bram Moolenaar0f873732019-12-05 20:28:46 +01003379 // set termcap codes to current text attributes
Bram Moolenaar071d4272004-06-13 20:20:40 +00003380 update_tcap(g_attrCurrent);
3381
3382 GetConsoleCursorInfo(g_hConOut, &g_cci);
3383 GetConsoleMode(g_hConIn, &g_cmodein);
3384 GetConsoleMode(g_hConOut, &g_cmodeout);
3385
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386 SaveConsoleTitleAndIcon();
3387 /*
3388 * Set both the small and big icons of the console window to Vim's icon.
3389 * Note that Vim presently only has one size of icon (32x32), but it
3390 * automatically gets scaled down to 16x16 when setting the small icon.
3391 */
3392 if (g_fCanChangeIcon)
3393 SetConsoleIcon(g_hWnd, g_hVimIcon, g_hVimIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394
3395 ui_get_shellsize();
3396
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003397# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003398 fdDump = fopen("dump", "wt");
3399
3400 if (fdDump)
3401 {
3402 time_t t;
3403
3404 time(&t);
3405 fputs(ctime(&t), fdDump);
3406 fflush(fdDump);
3407 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003408# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409
3410 g_fWindInitCalled = TRUE;
3411
Bram Moolenaar071d4272004-06-13 20:20:40 +00003412 g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003414# ifdef FEAT_CLIPBOARD
Bram Moolenaar693e40c2013-02-26 14:56:42 +01003415 win_clip_init();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003416# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417}
3418
3419/*
3420 * non-GUI version of mch_exit().
3421 * Shut down and exit with status `r'
3422 * Careful: mch_exit() may be called before mch_init()!
3423 */
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003424 static void
3425mch_exit_c(int r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003426{
Bram Moolenaar955f1982017-02-05 15:10:51 +01003427 exiting = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428
Bram Moolenaarcafafb32018-02-22 21:07:09 +01003429 vtp_exit();
3430
Bram Moolenaar955f1982017-02-05 15:10:51 +01003431 stoptermcap();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432 if (g_fWindInitCalled)
3433 settmode(TMODE_COOK);
3434
Bram Moolenaar0f873732019-12-05 20:28:46 +01003435 ml_close_all(TRUE); // remove all memfiles
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436
3437 if (g_fWindInitCalled)
3438 {
Bram Moolenaar40385db2018-08-07 22:31:44 +02003439 mch_restore_title(SAVE_RESTORE_BOTH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440 /*
3441 * Restore both the small and big icons of the console window to
3442 * what they were at startup. Don't do this when the window is
3443 * closed, Vim would hang here.
3444 */
3445 if (g_fCanChangeIcon && !g_fForceExit)
3446 SetConsoleIcon(g_hWnd, g_hOrigIconSmall, g_hOrigIcon);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003448# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449 if (fdDump)
3450 {
3451 time_t t;
3452
3453 time(&t);
3454 fputs(ctime(&t), fdDump);
3455 fclose(fdDump);
3456 }
3457 fdDump = NULL;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003458# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003459 }
3460
3461 SetConsoleCursorInfo(g_hConOut, &g_cci);
Wez Furlong6ef5ab52021-05-30 19:29:41 +02003462 SetConsoleMode(g_hConIn, g_cmodein | ENABLE_EXTENDED_FLAGS);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003463 SetConsoleMode(g_hConOut, g_cmodeout);
3464
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003465# ifdef DYNAMIC_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +00003466 dyn_libintl_end();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003467# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468
3469 exit(r);
3470}
Bram Moolenaar0f873732019-12-05 20:28:46 +01003471#endif // !FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00003472
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003473 void
3474mch_init(void)
3475{
3476#ifdef VIMDLL
3477 if (gui.starting)
3478 mch_init_g();
3479 else
3480 mch_init_c();
3481#elif defined(FEAT_GUI_MSWIN)
3482 mch_init_g();
3483#else
3484 mch_init_c();
3485#endif
3486}
3487
3488 void
3489mch_exit(int r)
3490{
Bram Moolenaar173d8412020-04-19 14:02:26 +02003491#ifdef FEAT_NETBEANS_INTG
3492 netbeans_send_disconnect();
3493#endif
3494
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003495#ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02003496 if (gui.in_use || gui.starting)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003497 mch_exit_g(r);
3498 else
3499 mch_exit_c(r);
3500#elif defined(FEAT_GUI_MSWIN)
3501 mch_exit_g(r);
3502#else
3503 mch_exit_c(r);
3504#endif
3505}
3506
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507/*
3508 * Do we have an interactive window?
3509 */
3510 int
3511mch_check_win(
Bram Moolenaar1266d672017-02-01 13:43:36 +01003512 int argc UNUSED,
3513 char **argv UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514{
3515 get_exe_name();
3516
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003517#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003518 return OK; // GUI always has a tty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519#else
Bram Moolenaarafde13b2019-04-28 19:46:49 +02003520# ifdef VIMDLL
3521 if (gui.in_use)
3522 return OK;
3523# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003524 if (isatty(1))
3525 return OK;
3526 return FAIL;
3527#endif
3528}
3529
Bram Moolenaar65f04f62013-08-30 17:29:16 +02003530/*
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003531 * Set the case of the file name, if it already exists.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532 * When "len" is > 0, also expand short to long filenames.
3533 */
3534 void
3535fname_case(
3536 char_u *name,
3537 int len)
3538{
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003539 int flen;
3540 WCHAR *p;
3541 WCHAR buf[_MAX_PATH + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003542
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +00003543 flen = (int)STRLEN(name);
Bram Moolenaar65f04f62013-08-30 17:29:16 +02003544 if (flen == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003545 return;
3546
3547 slash_adjust(name);
3548
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003549 p = enc_to_utf16(name, NULL);
3550 if (p == NULL)
Bram Moolenaar65f04f62013-08-30 17:29:16 +02003551 return;
3552
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003553 if (GetLongPathNameW(p, buf, _MAX_PATH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003554 {
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003555 char_u *q = utf16_to_enc(buf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003556
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003557 if (q != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003558 {
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003559 if (len > 0 || flen >= (int)STRLEN(q))
3560 vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
3561 vim_free(q);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562 }
3563 }
Bram Moolenaar8bb41b32019-03-30 17:28:16 +01003564 vim_free(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003565}
3566
3567
3568/*
3569 * Insert user name in s[len].
3570 */
3571 int
3572mch_get_user_name(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003573 char_u *s,
3574 int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575{
Bram Moolenaar0f873732019-12-05 20:28:46 +01003576 WCHAR wszUserName[256 + 1]; // UNLEN is 256
K.Takataeeec2542021-06-02 13:28:16 +02003577 DWORD wcch = ARRAY_LENGTH(wszUserName);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003578
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003579 if (GetUserNameW(wszUserName, &wcch))
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003580 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003581 char_u *p = utf16_to_enc(wszUserName, NULL);
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003582
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003583 if (p != NULL)
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003584 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003585 vim_strncpy(s, p, len - 1);
3586 vim_free(p);
3587 return OK;
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003588 }
Bram Moolenaarc8020ee2013-12-11 18:18:06 +01003589 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590 s[0] = NUL;
3591 return FAIL;
3592}
3593
3594
3595/*
3596 * Insert host name in s[len].
3597 */
3598 void
3599mch_get_host_name(
3600 char_u *s,
3601 int len)
3602{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003603 WCHAR wszHostName[256 + 1];
K.Takataeeec2542021-06-02 13:28:16 +02003604 DWORD wcch = ARRAY_LENGTH(wszHostName);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003605
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003606 if (GetComputerNameW(wszHostName, &wcch))
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003607 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003608 char_u *p = utf16_to_enc(wszHostName, NULL);
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003609
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003610 if (p != NULL)
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003611 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003612 vim_strncpy(s, p, len - 1);
3613 vim_free(p);
3614 return;
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003615 }
Bram Moolenaar2cc87382013-12-11 18:21:45 +01003616 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617}
3618
3619
3620/*
3621 * return process ID
3622 */
3623 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003624mch_get_pid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625{
3626 return (long)GetCurrentProcessId();
3627}
3628
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02003629/*
3630 * return TRUE if process "pid" is still running
3631 */
3632 int
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02003633mch_process_running(long pid)
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02003634{
3635 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, (DWORD)pid);
3636 DWORD status = 0;
3637 int ret = FALSE;
3638
3639 if (hProcess == NULL)
3640 return FALSE; // might not have access
3641 if (GetExitCodeProcess(hProcess, &status) )
3642 ret = status == STILL_ACTIVE;
3643 CloseHandle(hProcess);
3644 return ret;
3645}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646
3647/*
3648 * Get name of current directory into buffer 'buf' of length 'len' bytes.
3649 * Return OK for success, FAIL for failure.
3650 */
3651 int
3652mch_dirname(
3653 char_u *buf,
3654 int len)
3655{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003656 WCHAR wbuf[_MAX_PATH + 1];
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003657
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658 /*
3659 * Originally this was:
3660 * return (getcwd(buf, len) != NULL ? OK : FAIL);
3661 * But the Win32s known bug list says that getcwd() doesn't work
3662 * so use the Win32 system call instead. <Negri>
3663 */
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003664 if (GetCurrentDirectoryW(_MAX_PATH, wbuf) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003666 WCHAR wcbuf[_MAX_PATH + 1];
3667 char_u *p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003669 if (GetLongPathNameW(wbuf, wcbuf, _MAX_PATH) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003670 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003671 p = utf16_to_enc(wcbuf, NULL);
3672 if (STRLEN(p) >= (size_t)len)
Bram Moolenaarcea1f9e2018-08-19 14:38:42 +02003673 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003674 // long path name is too long, fall back to short one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 vim_free(p);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003676 p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677 }
3678 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003679 if (p == NULL)
3680 p = utf16_to_enc(wbuf, NULL);
Bram Moolenaar3b9fcfc2018-08-18 20:20:27 +02003681
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003682 if (p != NULL)
3683 {
3684 vim_strncpy(buf, p, len - 1);
3685 vim_free(p);
3686 return OK;
3687 }
3688 }
3689 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690}
3691
3692/*
Bram Moolenaarffa22202013-11-21 12:34:11 +01003693 * Get file permissions for "name".
3694 * Return mode_t or -1 for error.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 */
3696 long
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00003697mch_getperm(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698{
Bram Moolenaar8767f522016-07-01 17:17:39 +02003699 stat_T st;
Bram Moolenaarffa22202013-11-21 12:34:11 +01003700 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003701
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01003702 n = mch_stat((char *)name, &st);
Bram Moolenaar78cf3f02014-01-10 18:16:07 +01003703 return n == 0 ? (long)(unsigned short)st.st_mode : -1L;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003704}
3705
3706
3707/*
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003708 * Set file permission for "name" to "perm".
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003709 *
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003710 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711 */
3712 int
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003713mch_setperm(char_u *name, long perm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003715 long n;
3716 WCHAR *p;
Bram Moolenaare24a9c02013-07-24 13:49:22 +02003717
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003718 p = enc_to_utf16(name, NULL);
3719 if (p == NULL)
3720 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003722 n = _wchmod(p, perm);
3723 vim_free(p);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003724 if (n == -1)
3725 return FAIL;
3726
3727 win32_set_archive(name);
3728
3729 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730}
3731
3732/*
3733 * Set hidden flag for "name".
3734 */
3735 void
3736mch_hide(char_u *name)
3737{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003738 int attrs = win32_getattrs(name);
3739 if (attrs == -1)
3740 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003742 attrs |= FILE_ATTRIBUTE_HIDDEN;
3743 win32_setattrs(name, attrs);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744}
3745
3746/*
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003747 * Return TRUE if file "name" exists and is hidden.
3748 */
3749 int
3750mch_ishidden(char_u *name)
3751{
3752 int f = win32_getattrs(name);
3753
3754 if (f == -1)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003755 return FALSE; // file does not exist at all
Bram Moolenaar8a52ba72015-11-02 14:45:56 +01003756
3757 return (f & FILE_ATTRIBUTE_HIDDEN) != 0;
3758}
3759
3760/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003761 * return TRUE if "name" is a directory
3762 * return FALSE if "name" is not a directory or upon error
3763 */
3764 int
3765mch_isdir(char_u *name)
3766{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003767 int f = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768
3769 if (f == -1)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003770 return FALSE; // file does not exist at all
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771
3772 return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
3773}
3774
3775/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003776 * return TRUE if "name" is a directory, NOT a symlink to a directory
3777 * return FALSE if "name" is not a directory
3778 * return FALSE for error
3779 */
3780 int
3781mch_isrealdir(char_u *name)
3782{
3783 return mch_isdir(name) && !mch_is_symbolic_link(name);
3784}
3785
3786/*
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003787 * Create directory "name".
3788 * Return 0 on success, -1 on error.
3789 */
3790 int
3791mch_mkdir(char_u *name)
3792{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003793 WCHAR *p;
3794 int retval;
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003795
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003796 p = enc_to_utf16(name, NULL);
3797 if (p == NULL)
3798 return -1;
3799 retval = _wmkdir(p);
3800 vim_free(p);
3801 return retval;
Bram Moolenaar3c9c99c2011-05-05 18:31:59 +02003802}
3803
3804/*
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003805 * Delete directory "name".
3806 * Return 0 on success, -1 on error.
3807 */
3808 int
3809mch_rmdir(char_u *name)
3810{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003811 WCHAR *p;
3812 int retval;
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003813
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003814 p = enc_to_utf16(name, NULL);
3815 if (p == NULL)
3816 return -1;
3817 retval = _wrmdir(p);
3818 vim_free(p);
3819 return retval;
Bram Moolenaar4cf76792016-01-16 22:02:57 +01003820}
3821
3822/*
Bram Moolenaar03f48552006-02-28 23:52:23 +00003823 * Return TRUE if file "fname" has more than one link.
3824 */
3825 int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003826mch_is_hard_link(char_u *fname)
Bram Moolenaar03f48552006-02-28 23:52:23 +00003827{
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003828 BY_HANDLE_FILE_INFORMATION info;
3829
3830 return win32_fileinfo(fname, &info) == FILEINFO_OK
3831 && info.nNumberOfLinks > 1;
3832}
3833
3834/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01003835 * Return TRUE if "name" is a symbolic link (or a junction).
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003836 */
3837 int
Bram Moolenaar203258c2016-01-17 22:15:16 +01003838mch_is_symbolic_link(char_u *name)
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003839{
3840 HANDLE hFind;
3841 int res = FALSE;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003842 DWORD fileFlags = 0, reparseTag = 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003843 WCHAR *wn;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003844 WIN32_FIND_DATAW findDataW;
3845
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003846 wn = enc_to_utf16(name, NULL);
3847 if (wn == NULL)
3848 return FALSE;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003849
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003850 hFind = FindFirstFileW(wn, &findDataW);
3851 vim_free(wn);
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003852 if (hFind != INVALID_HANDLE_VALUE)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003853 {
3854 fileFlags = findDataW.dwFileAttributes;
3855 reparseTag = findDataW.dwReserved0;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003856 FindClose(hFind);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003857 }
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003858
3859 if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT)
Bram Moolenaar203258c2016-01-17 22:15:16 +01003860 && (reparseTag == IO_REPARSE_TAG_SYMLINK
3861 || reparseTag == IO_REPARSE_TAG_MOUNT_POINT))
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003862 res = TRUE;
3863
3864 return res;
3865}
3866
3867/*
3868 * Return TRUE if file "fname" has more than one link or if it is a symbolic
3869 * link.
3870 */
3871 int
3872mch_is_linked(char_u *fname)
3873{
3874 if (mch_is_hard_link(fname) || mch_is_symbolic_link(fname))
3875 return TRUE;
3876 return FALSE;
3877}
3878
3879/*
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003880 * Get the by-handle-file-information for "fname".
3881 * Returns FILEINFO_OK when OK.
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003882 * Returns FILEINFO_ENC_FAIL when enc_to_utf16() failed.
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003883 * Returns FILEINFO_READ_FAIL when CreateFile() failed.
3884 * Returns FILEINFO_INFO_FAIL when GetFileInformationByHandle() failed.
3885 */
3886 int
3887win32_fileinfo(char_u *fname, BY_HANDLE_FILE_INFORMATION *info)
3888{
Bram Moolenaar03f48552006-02-28 23:52:23 +00003889 HANDLE hFile;
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003890 int res = FILEINFO_READ_FAIL;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003891 WCHAR *wn;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003892
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003893 wn = enc_to_utf16(fname, NULL);
3894 if (wn == NULL)
3895 return FILEINFO_ENC_FAIL;
3896
3897 hFile = CreateFileW(wn, // file name
3898 GENERIC_READ, // access mode
3899 FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
3900 NULL, // security descriptor
3901 OPEN_EXISTING, // creation disposition
3902 FILE_FLAG_BACKUP_SEMANTICS, // file attributes
3903 NULL); // handle to template file
3904 vim_free(wn);
Bram Moolenaar03f48552006-02-28 23:52:23 +00003905
3906 if (hFile != INVALID_HANDLE_VALUE)
3907 {
Bram Moolenaar1c32dff2011-05-05 16:41:24 +02003908 if (GetFileInformationByHandle(hFile, info) != 0)
3909 res = FILEINFO_OK;
3910 else
3911 res = FILEINFO_INFO_FAIL;
Bram Moolenaar03f48552006-02-28 23:52:23 +00003912 CloseHandle(hFile);
3913 }
3914
Bram Moolenaar03f48552006-02-28 23:52:23 +00003915 return res;
3916}
3917
3918/*
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003919 * get file attributes for `name'
3920 * -1 : error
3921 * else FILE_ATTRIBUTE_* defined in winnt.h
3922 */
Bram Moolenaarffa22202013-11-21 12:34:11 +01003923 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003924win32_getattrs(char_u *name)
3925{
3926 int attr;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003927 WCHAR *p;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003928
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003929 p = enc_to_utf16(name, NULL);
3930 if (p == NULL)
3931 return INVALID_FILE_ATTRIBUTES;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003932
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003933 attr = GetFileAttributesW(p);
3934 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003935
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003936 return attr;
3937}
3938
3939/*
3940 * set file attributes for `name' to `attrs'
3941 *
3942 * return -1 for failure, 0 otherwise
3943 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003944 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003945win32_setattrs(char_u *name, int attrs)
3946{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003947 int res;
3948 WCHAR *p;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003949
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003950 p = enc_to_utf16(name, NULL);
3951 if (p == NULL)
3952 return -1;
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003953
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02003954 res = SetFileAttributesW(p, attrs);
3955 vim_free(p);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02003956
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003957 return res ? 0 : -1;
3958}
3959
3960/*
3961 * Set archive flag for "name".
3962 */
Bram Moolenaar6dff58f2018-09-30 21:43:26 +02003963 static int
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003964win32_set_archive(char_u *name)
3965{
3966 int attrs = win32_getattrs(name);
3967 if (attrs == -1)
3968 return -1;
3969
3970 attrs |= FILE_ATTRIBUTE_ARCHIVE;
3971 return win32_setattrs(name, attrs);
3972}
3973
3974/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975 * Return TRUE if file or directory "name" is writable (not readonly).
3976 * Strange semantics of Win32: a readonly directory is writable, but you can't
3977 * delete a file. Let's say this means it is writable.
3978 */
3979 int
3980mch_writable(char_u *name)
3981{
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003982 int attrs = win32_getattrs(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003983
Bram Moolenaar12b559e2013-06-12 22:41:37 +02003984 return (attrs != -1 && (!(attrs & FILE_ATTRIBUTE_READONLY)
3985 || (attrs & FILE_ATTRIBUTE_DIRECTORY)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986}
3987
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988/*
Bram Moolenaar43663192017-03-05 14:29:12 +01003989 * Return TRUE if "name" can be executed, FALSE if not.
Bram Moolenaar77b77102015-03-21 22:18:41 +01003990 * If "use_path" is FALSE only check if "name" is executable.
Bram Moolenaar43663192017-03-05 14:29:12 +01003991 * When returning TRUE and "path" is not NULL save the path and set "*path" to
3992 * the allocated memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993 */
3994 int
Yegappan Lakshmananebb01bd2022-06-08 15:14:09 +01003995mch_can_exe(char_u *name, char_u **path, int use_path UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003996{
Bram Moolenaar95da1362020-05-30 18:37:55 +02003997 return executable_exists((char *)name, path, TRUE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003998}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003999
4000/*
4001 * Check what "name" is:
4002 * NODE_NORMAL: file or directory (or doesn't exist)
4003 * NODE_WRITABLE: writable device, socket, fifo, etc.
4004 * NODE_OTHER: non-writable things
4005 */
4006 int
4007mch_nodetype(char_u *name)
4008{
4009 HANDLE hFile;
4010 int type;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004011 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004012
Bram Moolenaar0f873732019-12-05 20:28:46 +01004013 // We can't open a file with a name "\\.\con" or "\\.\prn" and trying to
4014 // read from it later will cause Vim to hang. Thus return NODE_WRITABLE
4015 // here.
Bram Moolenaar043545e2006-10-10 16:44:07 +00004016 if (STRNCMP(name, "\\\\.\\", 4) == 0)
4017 return NODE_WRITABLE;
4018
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004019 wn = enc_to_utf16(name, NULL);
4020 if (wn == NULL)
4021 return NODE_NORMAL;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004022
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004023 hFile = CreateFileW(wn, // file name
4024 GENERIC_WRITE, // access mode
4025 0, // share mode
4026 NULL, // security descriptor
4027 OPEN_EXISTING, // creation disposition
4028 0, // file attributes
4029 NULL); // handle to template file
4030 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031 if (hFile == INVALID_HANDLE_VALUE)
4032 return NODE_NORMAL;
4033
4034 type = GetFileType(hFile);
4035 CloseHandle(hFile);
4036 if (type == FILE_TYPE_CHAR)
4037 return NODE_WRITABLE;
4038 if (type == FILE_TYPE_DISK)
4039 return NODE_NORMAL;
4040 return NODE_OTHER;
4041}
4042
4043#ifdef HAVE_ACL
4044struct my_acl
4045{
4046 PSECURITY_DESCRIPTOR pSecurityDescriptor;
4047 PSID pSidOwner;
4048 PSID pSidGroup;
4049 PACL pDacl;
4050 PACL pSacl;
4051};
4052#endif
4053
4054/*
4055 * Return a pointer to the ACL of file "fname" in allocated memory.
4056 * Return NULL if the ACL is not available for whatever reason.
4057 */
4058 vim_acl_T
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004059mch_get_acl(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060{
4061#ifndef HAVE_ACL
4062 return (vim_acl_T)NULL;
4063#else
4064 struct my_acl *p = NULL;
Bram Moolenaar27515922013-06-29 15:36:26 +02004065 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004066
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004067 p = ALLOC_CLEAR_ONE(struct my_acl);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004068 if (p != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004070 WCHAR *wn;
Bram Moolenaar27515922013-06-29 15:36:26 +02004071
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004072 wn = enc_to_utf16(fname, NULL);
4073 if (wn == NULL)
Bram Moolenaarbbf9f342020-11-10 22:03:40 +01004074 {
4075 vim_free(p);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004076 return NULL;
Bram Moolenaarbbf9f342020-11-10 22:03:40 +01004077 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004078
4079 // Try to retrieve the entire security descriptor.
4080 err = GetNamedSecurityInfoW(
4081 wn, // Abstract filename
4082 SE_FILE_OBJECT, // File Object
4083 OWNER_SECURITY_INFORMATION |
4084 GROUP_SECURITY_INFORMATION |
4085 DACL_SECURITY_INFORMATION |
4086 SACL_SECURITY_INFORMATION,
4087 &p->pSidOwner, // Ownership information.
4088 &p->pSidGroup, // Group membership.
4089 &p->pDacl, // Discretionary information.
4090 &p->pSacl, // For auditing purposes.
4091 &p->pSecurityDescriptor);
4092 if (err == ERROR_ACCESS_DENIED ||
4093 err == ERROR_PRIVILEGE_NOT_HELD)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004094 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004095 // Retrieve only DACL.
4096 (void)GetNamedSecurityInfoW(
4097 wn,
4098 SE_FILE_OBJECT,
4099 DACL_SECURITY_INFORMATION,
4100 NULL,
4101 NULL,
4102 &p->pDacl,
4103 NULL,
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004104 &p->pSecurityDescriptor);
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004105 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004106 if (p->pSecurityDescriptor == NULL)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004107 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004108 mch_free_acl((vim_acl_T)p);
4109 p = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004111 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 }
4113
4114 return (vim_acl_T)p;
4115#endif
4116}
4117
Bram Moolenaar27515922013-06-29 15:36:26 +02004118#ifdef HAVE_ACL
4119/*
4120 * Check if "acl" contains inherited ACE.
4121 */
4122 static BOOL
4123is_acl_inherited(PACL acl)
4124{
4125 DWORD i;
4126 ACL_SIZE_INFORMATION acl_info;
4127 PACCESS_ALLOWED_ACE ace;
4128
4129 acl_info.AceCount = 0;
4130 GetAclInformation(acl, &acl_info, sizeof(acl_info), AclSizeInformation);
4131 for (i = 0; i < acl_info.AceCount; i++)
4132 {
4133 GetAce(acl, i, (LPVOID *)&ace);
4134 if (ace->Header.AceFlags & INHERITED_ACE)
4135 return TRUE;
4136 }
4137 return FALSE;
4138}
4139#endif
4140
Bram Moolenaar071d4272004-06-13 20:20:40 +00004141/*
4142 * Set the ACL of file "fname" to "acl" (unless it's NULL).
4143 * Errors are ignored.
4144 * This must only be called with "acl" equal to what mch_get_acl() returned.
4145 */
4146 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004147mch_set_acl(char_u *fname, vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148{
4149#ifdef HAVE_ACL
4150 struct my_acl *p = (struct my_acl *)acl;
Bram Moolenaar27515922013-06-29 15:36:26 +02004151 SECURITY_INFORMATION sec_info = 0;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004152 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004154 if (p == NULL)
4155 return;
4156
4157 wn = enc_to_utf16(fname, NULL);
4158 if (wn == NULL)
4159 return;
4160
4161 // Set security flags
4162 if (p->pSidOwner)
4163 sec_info |= OWNER_SECURITY_INFORMATION;
4164 if (p->pSidGroup)
4165 sec_info |= GROUP_SECURITY_INFORMATION;
4166 if (p->pDacl)
Bram Moolenaar27515922013-06-29 15:36:26 +02004167 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004168 sec_info |= DACL_SECURITY_INFORMATION;
4169 // Do not inherit its parent's DACL.
4170 // If the DACL is inherited, Cygwin permissions would be changed.
4171 if (!is_acl_inherited(p->pDacl))
4172 sec_info |= PROTECTED_DACL_SECURITY_INFORMATION;
Bram Moolenaar27515922013-06-29 15:36:26 +02004173 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004174 if (p->pSacl)
4175 sec_info |= SACL_SECURITY_INFORMATION;
4176
4177 (void)SetNamedSecurityInfoW(
4178 wn, // Abstract filename
4179 SE_FILE_OBJECT, // File Object
4180 sec_info,
4181 p->pSidOwner, // Ownership information.
4182 p->pSidGroup, // Group membership.
4183 p->pDacl, // Discretionary information.
4184 p->pSacl // For auditing purposes.
4185 );
4186 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187#endif
4188}
4189
4190 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004191mch_free_acl(vim_acl_T acl)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192{
4193#ifdef HAVE_ACL
4194 struct my_acl *p = (struct my_acl *)acl;
4195
4196 if (p != NULL)
4197 {
4198 LocalFree(p->pSecurityDescriptor); // Free the memory just in case
4199 vim_free(p);
4200 }
4201#endif
4202}
4203
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004204#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205
4206/*
4207 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
4208 */
4209 static BOOL WINAPI
4210handler_routine(
4211 DWORD dwCtrlType)
4212{
Bram Moolenaar589b1102017-08-12 16:39:05 +02004213 INPUT_RECORD ir;
4214 DWORD out;
4215
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 switch (dwCtrlType)
4217 {
4218 case CTRL_C_EVENT:
4219 if (ctrl_c_interrupts)
4220 g_fCtrlCPressed = TRUE;
4221 return TRUE;
4222
4223 case CTRL_BREAK_EVENT:
4224 g_fCBrkPressed = TRUE;
Bram Moolenaar589b1102017-08-12 16:39:05 +02004225 ctrl_break_was_pressed = TRUE;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004226 // ReadConsoleInput is blocking, send a key event to continue.
Bram Moolenaar589b1102017-08-12 16:39:05 +02004227 ir.EventType = KEY_EVENT;
4228 ir.Event.KeyEvent.bKeyDown = TRUE;
4229 ir.Event.KeyEvent.wRepeatCount = 1;
4230 ir.Event.KeyEvent.wVirtualKeyCode = VK_CANCEL;
4231 ir.Event.KeyEvent.wVirtualScanCode = 0;
4232 ir.Event.KeyEvent.dwControlKeyState = 0;
4233 ir.Event.KeyEvent.uChar.UnicodeChar = 0;
4234 WriteConsoleInput(g_hConIn, &ir, 1, &out);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004235 return TRUE;
4236
Bram Moolenaar0f873732019-12-05 20:28:46 +01004237 // fatal events: shut down gracefully
Bram Moolenaar071d4272004-06-13 20:20:40 +00004238 case CTRL_CLOSE_EVENT:
4239 case CTRL_LOGOFF_EVENT:
4240 case CTRL_SHUTDOWN_EVENT:
4241 windgoto((int)Rows - 1, 0);
4242 g_fForceExit = TRUE;
4243
Bram Moolenaar0fde2902008-03-16 13:54:13 +00004244 vim_snprintf((char *)IObuff, IOSIZE, _("Vim: Caught %s event\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 (dwCtrlType == CTRL_CLOSE_EVENT
4246 ? _("close")
4247 : dwCtrlType == CTRL_LOGOFF_EVENT
4248 ? _("logoff")
4249 : _("shutdown")));
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004250# ifdef DEBUG
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 OutputDebugString(IObuff);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004252# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253
Bram Moolenaar0f873732019-12-05 20:28:46 +01004254 preserve_exit(); // output IObuff, preserve files and exit
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255
Bram Moolenaar0f873732019-12-05 20:28:46 +01004256 return TRUE; // not reached
Bram Moolenaar071d4272004-06-13 20:20:40 +00004257
4258 default:
4259 return FALSE;
4260 }
4261}
4262
4263
4264/*
4265 * set the tty in (raw) ? "raw" : "cooked" mode
4266 */
4267 void
Bram Moolenaar26e86442020-05-17 14:06:16 +02004268mch_settmode(tmode_T tmode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269{
4270 DWORD cmodein;
4271 DWORD cmodeout;
4272 BOOL bEnableHandler;
4273
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004274# ifdef VIMDLL
4275 if (gui.in_use)
4276 return;
4277# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 GetConsoleMode(g_hConIn, &cmodein);
4279 GetConsoleMode(g_hConOut, &cmodeout);
4280 if (tmode == TMODE_RAW)
4281 {
4282 cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
4283 ENABLE_ECHO_INPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 if (g_fMouseActive)
Wez Furlong6ef5ab52021-05-30 19:29:41 +02004285 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004286 cmodein |= ENABLE_MOUSE_INPUT;
Wez Furlong6ef5ab52021-05-30 19:29:41 +02004287 cmodein &= ~ENABLE_QUICK_EDIT_MODE;
4288 }
4289 else
4290 {
4291 cmodein |= g_cmodein & ENABLE_QUICK_EDIT_MODE;
4292 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01004293 cmodeout &= ~(
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004294# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar0f873732019-12-05 20:28:46 +01004295 // Do not turn off the ENABLE_PROCESSED_OUTPUT flag when using
4296 // VTP.
Bram Moolenaarcafafb32018-02-22 21:07:09 +01004297 ((vtp_working) ? 0 : ENABLE_PROCESSED_OUTPUT) |
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004298# else
Bram Moolenaarcafafb32018-02-22 21:07:09 +01004299 ENABLE_PROCESSED_OUTPUT |
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004300# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01004301 ENABLE_WRAP_AT_EOL_OUTPUT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302 bEnableHandler = TRUE;
4303 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004304 else // cooked
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 {
4306 cmodein |= (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
4307 ENABLE_ECHO_INPUT);
4308 cmodeout |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
4309 bEnableHandler = FALSE;
4310 }
Wez Furlong6ef5ab52021-05-30 19:29:41 +02004311 SetConsoleMode(g_hConIn, cmodein | ENABLE_EXTENDED_FLAGS);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312 SetConsoleMode(g_hConOut, cmodeout);
4313 SetConsoleCtrlHandler(handler_routine, bEnableHandler);
4314
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004315# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316 if (fdDump)
4317 {
4318 fprintf(fdDump, "mch_settmode(%s, in = %x, out = %x)\n",
4319 tmode == TMODE_RAW ? "raw" :
4320 tmode == TMODE_COOK ? "cooked" : "normal",
4321 cmodein, cmodeout);
4322 fflush(fdDump);
4323 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004324# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325}
4326
4327
4328/*
4329 * Get the size of the current window in `Rows' and `Columns'
4330 * Return OK when size could be determined, FAIL otherwise.
4331 */
4332 int
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004333mch_get_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334{
4335 CONSOLE_SCREEN_BUFFER_INFO csbi;
4336
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004337# ifdef VIMDLL
4338 if (gui.in_use)
4339 return OK;
4340# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 if (!g_fTermcapMode && g_cbTermcap.IsValid)
4342 {
4343 /*
4344 * For some reason, we are trying to get the screen dimensions
4345 * even though we are not in termcap mode. The 'Rows' and 'Columns'
4346 * variables are really intended to mean the size of Vim screen
4347 * while in termcap mode.
4348 */
4349 Rows = g_cbTermcap.Info.dwSize.Y;
4350 Columns = g_cbTermcap.Info.dwSize.X;
4351 }
4352 else if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
4353 {
4354 Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
4355 Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
4356 }
4357 else
4358 {
4359 Rows = 25;
4360 Columns = 80;
4361 }
4362 return OK;
4363}
4364
4365/*
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004366 * Resize console buffer to 'COORD'
4367 */
4368 static void
4369ResizeConBuf(
4370 HANDLE hConsole,
4371 COORD coordScreen)
4372{
4373 if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
4374 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004375# ifdef MCH_WRITE_DUMP
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004376 if (fdDump)
4377 {
4378 fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
4379 GetLastError());
4380 fflush(fdDump);
4381 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004382# endif
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004383 }
4384}
4385
4386/*
4387 * Resize console window size to 'srWindowRect'
4388 */
4389 static void
4390ResizeWindow(
4391 HANDLE hConsole,
4392 SMALL_RECT srWindowRect)
4393{
4394 if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect))
4395 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004396# ifdef MCH_WRITE_DUMP
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004397 if (fdDump)
4398 {
4399 fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
4400 GetLastError());
4401 fflush(fdDump);
4402 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004403# endif
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004404 }
4405}
4406
4407/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408 * Set a console window to `xSize' * `ySize'
4409 */
4410 static void
4411ResizeConBufAndWindow(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004412 HANDLE hConsole,
4413 int xSize,
4414 int ySize)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415{
Bram Moolenaar0f873732019-12-05 20:28:46 +01004416 CONSOLE_SCREEN_BUFFER_INFO csbi; // hold current console buffer info
4417 SMALL_RECT srWindowRect; // hold the new console size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 COORD coordScreen;
Bram Moolenaarf49a6922019-07-15 20:37:05 +02004419 COORD cursor;
Bram Moolenaar2551c032018-08-23 22:38:31 +02004420 static int resized = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004421
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004422# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423 if (fdDump)
4424 {
4425 fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
4426 fflush(fdDump);
4427 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004428# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429
Bram Moolenaar0f873732019-12-05 20:28:46 +01004430 // get the largest size we can size the console window to
Bram Moolenaar071d4272004-06-13 20:20:40 +00004431 coordScreen = GetLargestConsoleWindowSize(hConsole);
4432
Bram Moolenaar0f873732019-12-05 20:28:46 +01004433 // define the new console window size and scroll position
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434 srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
4435 srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
4436 srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
4437
4438 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
4439 {
4440 int sx, sy;
4441
4442 sx = csbi.srWindow.Right - csbi.srWindow.Left + 1;
4443 sy = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
4444 if (sy < ySize || sx < xSize)
4445 {
4446 /*
4447 * Increasing number of lines/columns, do buffer first.
4448 * Use the maximal size in x and y direction.
4449 */
4450 if (sy < ySize)
4451 coordScreen.Y = ySize;
4452 else
4453 coordScreen.Y = sy;
4454 if (sx < xSize)
4455 coordScreen.X = xSize;
4456 else
4457 coordScreen.X = sx;
4458 SetConsoleScreenBufferSize(hConsole, coordScreen);
4459 }
4460 }
4461
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004462 // define the new console buffer size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 coordScreen.X = xSize;
4464 coordScreen.Y = ySize;
4465
Bram Moolenaar2551c032018-08-23 22:38:31 +02004466 // In the new console call API, only the first time in reverse order
4467 if (!vtp_working || resized)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 {
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004469 ResizeWindow(hConsole, srWindowRect);
4470 ResizeConBuf(hConsole, coordScreen);
4471 }
4472 else
4473 {
Bram Moolenaarf49a6922019-07-15 20:37:05 +02004474 // Workaround for a Windows 10 bug
4475 cursor.X = srWindowRect.Left;
4476 cursor.Y = srWindowRect.Top;
4477 SetConsoleCursorPosition(hConsole, cursor);
4478
Bram Moolenaarb1cf1612018-08-07 20:47:16 +02004479 ResizeConBuf(hConsole, coordScreen);
4480 ResizeWindow(hConsole, srWindowRect);
Bram Moolenaar2551c032018-08-23 22:38:31 +02004481 resized = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 }
4483}
4484
4485
4486/*
4487 * Set the console window to `Rows' * `Columns'
4488 */
4489 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004490mch_set_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491{
4492 COORD coordScreen;
4493
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004494# ifdef VIMDLL
4495 if (gui.in_use)
4496 return;
4497# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01004498 // Don't change window size while still starting up
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 if (suppress_winsize != 0)
4500 {
4501 suppress_winsize = 2;
4502 return;
4503 }
4504
4505 if (term_console)
4506 {
4507 coordScreen = GetLargestConsoleWindowSize(g_hConOut);
4508
Bram Moolenaar0f873732019-12-05 20:28:46 +01004509 // Clamp Rows and Columns to reasonable values
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510 if (Rows > coordScreen.Y)
4511 Rows = coordScreen.Y;
4512 if (Columns > coordScreen.X)
4513 Columns = coordScreen.X;
4514
4515 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
4516 }
4517}
4518
4519/*
4520 * Rows and/or Columns has changed.
4521 */
4522 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004523mch_new_shellsize(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004525# ifdef VIMDLL
4526 if (gui.in_use)
4527 return;
4528# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 set_scroll_region(0, 0, Columns - 1, Rows - 1);
4530}
4531
4532
4533/*
4534 * Called when started up, to set the winsize that was delayed.
4535 */
4536 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00004537mch_set_winsize_now(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538{
4539 if (suppress_winsize == 2)
4540 {
4541 suppress_winsize = 0;
4542 mch_set_shellsize();
4543 shell_resized();
4544 }
4545 suppress_winsize = 0;
4546}
Bram Moolenaar0f873732019-12-05 20:28:46 +01004547#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004549 static BOOL
4550vim_create_process(
Bram Moolenaar36c85b22013-12-12 20:25:44 +01004551 char *cmd,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004552 BOOL inherit_handles,
Bram Moolenaar3f1138e2014-01-05 13:29:26 +01004553 DWORD flags,
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004554 STARTUPINFO *si,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004555 PROCESS_INFORMATION *pi,
4556 LPVOID *env,
4557 char *cwd)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004558{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004559 BOOL ret = FALSE;
4560 WCHAR *wcmd, *wcwd = NULL;
4561
4562 wcmd = enc_to_utf16((char_u *)cmd, NULL);
4563 if (wcmd == NULL)
4564 return FALSE;
4565 if (cwd != NULL)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004566 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004567 wcwd = enc_to_utf16((char_u *)cwd, NULL);
4568 if (wcwd == NULL)
4569 goto theend;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004570 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004571
4572 ret = CreateProcessW(
4573 NULL, // Executable name
4574 wcmd, // Command to execute
4575 NULL, // Process security attributes
4576 NULL, // Thread security attributes
4577 inherit_handles, // Inherit handles
4578 flags, // Creation flags
4579 env, // Environment
4580 wcwd, // Current directory
4581 (LPSTARTUPINFOW)si, // Startup information
4582 pi); // Process information
4583theend:
4584 vim_free(wcmd);
4585 vim_free(wcwd);
4586 return ret;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004587}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004588
4589
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004590 static HINSTANCE
4591vim_shell_execute(
4592 char *cmd,
4593 INT n_show_cmd)
4594{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02004595 HINSTANCE ret;
4596 WCHAR *wcmd;
4597
4598 wcmd = enc_to_utf16((char_u *)cmd, NULL);
4599 if (wcmd == NULL)
4600 return (HINSTANCE) 0;
4601
4602 ret = ShellExecuteW(NULL, NULL, wcmd, NULL, NULL, n_show_cmd);
4603 vim_free(wcmd);
4604 return ret;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01004605}
4606
4607
Bram Moolenaar4f974752019-02-17 17:44:42 +01004608#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004609
4610/*
4611 * Specialised version of system() for Win32 GUI mode.
4612 * This version proceeds as follows:
4613 * 1. Create a console window for use by the subprocess
4614 * 2. Run the subprocess (it gets the allocated console by default)
4615 * 3. Wait for the subprocess to terminate and get its exit code
4616 * 4. Prompt the user to press a key to close the console window
4617 */
4618 static int
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004619mch_system_classic(char *cmd, int options)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004620{
4621 STARTUPINFO si;
4622 PROCESS_INFORMATION pi;
4623 DWORD ret = 0;
4624 HWND hwnd = GetFocus();
4625
4626 si.cb = sizeof(si);
4627 si.lpReserved = NULL;
4628 si.lpDesktop = NULL;
4629 si.lpTitle = NULL;
4630 si.dwFlags = STARTF_USESHOWWINDOW;
4631 /*
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004632 * It's nicer to run a filter command in a minimized window.
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004633 * Don't activate the window to keep focus on Vim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634 */
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004635 if (options & SHELL_DOOUT)
Bram Moolenaar96e5cee2010-11-24 12:35:21 +01004636 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637 else
4638 si.wShowWindow = SW_SHOWNORMAL;
4639 si.cbReserved2 = 0;
4640 si.lpReserved2 = NULL;
4641
Bram Moolenaar0f873732019-12-05 20:28:46 +01004642 // Now, run the command
Bram Moolenaar910cffb2013-12-11 17:58:35 +01004643 vim_create_process(cmd, FALSE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004644 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE,
4645 &si, &pi, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646
Bram Moolenaar0f873732019-12-05 20:28:46 +01004647 // Wait for the command to terminate before continuing
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004649# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650 int delay = 1;
4651
Bram Moolenaar0f873732019-12-05 20:28:46 +01004652 // Keep updating the window while waiting for the shell to finish.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004653 for (;;)
4654 {
4655 MSG msg;
4656
K.Takatab7057bd2022-01-21 11:37:07 +00004657 if (PeekMessageW(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004658 {
4659 TranslateMessage(&msg);
K.Takatab7057bd2022-01-21 11:37:07 +00004660 DispatchMessageW(&msg);
Bram Moolenaare4195c52012-08-02 12:31:44 +02004661 delay = 1;
4662 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004663 }
4664 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
4665 break;
4666
Bram Moolenaar0f873732019-12-05 20:28:46 +01004667 // We start waiting for a very short time and then increase it, so
4668 // that we respond quickly when the process is quick, and don't
4669 // consume too much overhead when it's slow.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670 if (delay < 50)
4671 delay += 10;
4672 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004673# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 WaitForSingleObject(pi.hProcess, INFINITE);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01004675# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676
Bram Moolenaar0f873732019-12-05 20:28:46 +01004677 // Get the command exit code
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678 GetExitCodeProcess(pi.hProcess, &ret);
4679 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004680
Bram Moolenaar0f873732019-12-05 20:28:46 +01004681 // Close the handles to the subprocess, so that it goes away
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682 CloseHandle(pi.hThread);
4683 CloseHandle(pi.hProcess);
4684
Bram Moolenaar0f873732019-12-05 20:28:46 +01004685 // Try to get input focus back. Doesn't always work though.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 PostMessage(hwnd, WM_SETFOCUS, 0, 0);
4687
4688 return ret;
4689}
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004690
4691/*
4692 * Thread launched by the gui to send the current buffer data to the
4693 * process. This way avoid to hang up vim totally if the children
4694 * process take a long time to process the lines.
4695 */
Bram Moolenaar4c38d662016-08-03 20:54:57 +02004696 static unsigned int __stdcall
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004697sub_process_writer(LPVOID param)
4698{
4699 HANDLE g_hChildStd_IN_Wr = param;
4700 linenr_T lnum = curbuf->b_op_start.lnum;
4701 DWORD len = 0;
4702 DWORD l;
4703 char_u *lp = ml_get(lnum);
4704 char_u *s;
4705 int written = 0;
4706
4707 for (;;)
4708 {
4709 l = (DWORD)STRLEN(lp + written);
4710 if (l == 0)
4711 len = 0;
4712 else if (lp[written] == NL)
4713 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004714 // NL -> NUL translation
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004715 WriteFile(g_hChildStd_IN_Wr, "", 1, &len, NULL);
4716 }
4717 else
4718 {
4719 s = vim_strchr(lp + written, NL);
4720 WriteFile(g_hChildStd_IN_Wr, (char *)lp + written,
4721 s == NULL ? l : (DWORD)(s - (lp + written)),
4722 &len, NULL);
4723 }
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +01004724 if (len == l)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004725 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004726 // Finished a line, add a NL, unless this line should not have
4727 // one.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004728 if (lnum != curbuf->b_op_end.lnum
Bram Moolenaar34d72d42015-07-17 14:18:08 +02004729 || (!curbuf->b_p_bin
4730 && curbuf->b_p_fixeol)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004731 || (lnum != curbuf->b_no_eol_lnum
4732 && (lnum != curbuf->b_ml.ml_line_count
4733 || curbuf->b_p_eol)))
4734 {
Bram Moolenaar42335f52018-09-13 15:33:43 +02004735 WriteFile(g_hChildStd_IN_Wr, "\n", 1,
4736 (LPDWORD)&vim_ignored, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004737 }
4738
4739 ++lnum;
4740 if (lnum > curbuf->b_op_end.lnum)
4741 break;
4742
4743 lp = ml_get(lnum);
4744 written = 0;
4745 }
4746 else if (len > 0)
4747 written += len;
4748 }
4749
Bram Moolenaar0f873732019-12-05 20:28:46 +01004750 // finished all the lines, close pipe
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004751 CloseHandle(g_hChildStd_IN_Wr);
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004752 return 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004753}
4754
4755
Bram Moolenaar0f873732019-12-05 20:28:46 +01004756# define BUFLEN 100 // length for buffer, stolen from unix version
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004757
4758/*
4759 * This function read from the children's stdout and write the
4760 * data on screen or in the buffer accordingly.
4761 */
4762 static void
4763dump_pipe(int options,
4764 HANDLE g_hChildStd_OUT_Rd,
4765 garray_T *ga,
4766 char_u buffer[],
4767 DWORD *buffer_off)
4768{
4769 DWORD availableBytes = 0;
4770 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004771 int ret;
4772 DWORD len;
4773 DWORD toRead;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004774
Bram Moolenaar0f873732019-12-05 20:28:46 +01004775 // we query the pipe to see if there is any data to read
4776 // to avoid to perform a blocking read
4777 ret = PeekNamedPipe(g_hChildStd_OUT_Rd, // pipe to query
4778 NULL, // optional buffer
4779 0, // buffer size
4780 NULL, // number of read bytes
4781 &availableBytes, // available bytes total
4782 NULL); // byteLeft
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004783
Bram Moolenaar0f873732019-12-05 20:28:46 +01004784 // We got real data in the pipe, read it
Bram Moolenaarf6a2b082012-06-29 13:14:03 +02004785 while (ret != 0 && availableBytes > 0)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004786 {
Bram Moolenaara12a1612019-01-24 16:39:02 +01004787 toRead = (DWORD)(BUFLEN - *buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004788 toRead = availableBytes < toRead ? availableBytes : toRead;
Bram Moolenaara12a1612019-01-24 16:39:02 +01004789 ReadFile(g_hChildStd_OUT_Rd, buffer + *buffer_off, toRead , &len, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004790
Bram Moolenaar0f873732019-12-05 20:28:46 +01004791 // If we haven't read anything, there is a problem
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004792 if (len == 0)
4793 break;
4794
4795 availableBytes -= len;
4796
4797 if (options & SHELL_READ)
4798 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004799 // Do NUL -> NL translation, append NL separated
4800 // lines to the current buffer.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004801 for (i = 0; i < len; ++i)
4802 {
4803 if (buffer[i] == NL)
4804 append_ga_line(ga);
4805 else if (buffer[i] == NUL)
4806 ga_append(ga, NL);
4807 else
4808 ga_append(ga, buffer[i]);
4809 }
4810 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004811 else if (has_mbyte)
4812 {
4813 int l;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02004814 int c;
4815 char_u *p;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004816
4817 len += *buffer_off;
4818 buffer[len] = NUL;
4819
Bram Moolenaar0f873732019-12-05 20:28:46 +01004820 // Check if the last character in buffer[] is
4821 // incomplete, keep these bytes for the next
4822 // round.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004823 for (p = buffer; p < buffer + len; p += l)
4824 {
Bram Moolenaard3c907b2016-08-17 21:32:09 +02004825 l = MB_CPTR2LEN(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004826 if (l == 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004827 l = 1; // NUL byte?
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004828 else if (MB_BYTE2LEN(*p) != l)
4829 break;
4830 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01004831 if (p == buffer) // no complete character
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004832 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01004833 // avoid getting stuck at an illegal byte
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004834 if (len >= 12)
4835 ++p;
4836 else
4837 {
4838 *buffer_off = len;
4839 return;
4840 }
4841 }
4842 c = *p;
4843 *p = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004844 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004845 if (p < buffer + len)
4846 {
4847 *p = c;
4848 *buffer_off = (DWORD)((buffer + len) - p);
4849 mch_memmove(buffer, p, *buffer_off);
4850 return;
4851 }
4852 *buffer_off = 0;
4853 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004854 else
4855 {
4856 buffer[len] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01004857 msg_puts((char *)buffer);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004858 }
4859
4860 windgoto(msg_row, msg_col);
4861 cursor_on();
4862 out_flush();
4863 }
4864}
4865
4866/*
4867 * Version of system to use for windows NT > 5.0 (Win2K), use pipe
4868 * for communication and doesn't open any new window.
4869 */
4870 static int
4871mch_system_piped(char *cmd, int options)
4872{
4873 STARTUPINFO si;
4874 PROCESS_INFORMATION pi;
4875 DWORD ret = 0;
4876
4877 HANDLE g_hChildStd_IN_Rd = NULL;
4878 HANDLE g_hChildStd_IN_Wr = NULL;
4879 HANDLE g_hChildStd_OUT_Rd = NULL;
4880 HANDLE g_hChildStd_OUT_Wr = NULL;
4881
Bram Moolenaar0f873732019-12-05 20:28:46 +01004882 char_u buffer[BUFLEN + 1]; // reading buffer + size
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004883 DWORD len;
4884
Bram Moolenaar0f873732019-12-05 20:28:46 +01004885 // buffer used to receive keys
4886 char_u ta_buf[BUFLEN + 1]; // TypeAHead
4887 int ta_len = 0; // valid bytes in ta_buf[]
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004888
4889 DWORD i;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004890 int noread_cnt = 0;
4891 garray_T ga;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01004892 int delay = 1;
Bram Moolenaar0f873732019-12-05 20:28:46 +01004893 DWORD buffer_off = 0; // valid bytes in buffer[]
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004894 char *p = NULL;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004895
4896 SECURITY_ATTRIBUTES saAttr;
4897
Bram Moolenaar0f873732019-12-05 20:28:46 +01004898 // Set the bInheritHandle flag so pipe handles are inherited.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004899 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
4900 saAttr.bInheritHandle = TRUE;
4901 saAttr.lpSecurityDescriptor = NULL;
4902
4903 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004904 // Ensure the read handle to the pipe for STDOUT is not inherited.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004905 || ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004906 // Create a pipe for the child process's STDIN.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004907 || ! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004908 // Ensure the write handle to the pipe for STDIN is not inherited.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02004909 || ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004910 {
4911 CloseHandle(g_hChildStd_IN_Rd);
4912 CloseHandle(g_hChildStd_IN_Wr);
4913 CloseHandle(g_hChildStd_OUT_Rd);
4914 CloseHandle(g_hChildStd_OUT_Wr);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004915 msg_puts(_("\nCannot create pipes\n"));
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004916 }
4917
4918 si.cb = sizeof(si);
4919 si.lpReserved = NULL;
4920 si.lpDesktop = NULL;
4921 si.lpTitle = NULL;
4922 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
4923
Bram Moolenaar0f873732019-12-05 20:28:46 +01004924 // set-up our file redirection
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004925 si.hStdError = g_hChildStd_OUT_Wr;
4926 si.hStdOutput = g_hChildStd_OUT_Wr;
4927 si.hStdInput = g_hChildStd_IN_Rd;
4928 si.wShowWindow = SW_HIDE;
4929 si.cbReserved2 = 0;
4930 si.lpReserved2 = NULL;
4931
4932 if (options & SHELL_READ)
4933 ga_init2(&ga, 1, BUFLEN);
4934
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004935 if (cmd != NULL)
4936 {
4937 p = (char *)vim_strsave((char_u *)cmd);
4938 if (p != NULL)
4939 unescape_shellxquote((char_u *)p, p_sxe);
4940 else
4941 p = cmd;
4942 }
4943
Bram Moolenaar0f873732019-12-05 20:28:46 +01004944 // Now, run the command.
4945 // About "Inherit handles" being TRUE: this command can be litigious,
4946 // handle inheritance was deactivated for pending temp file, but, if we
4947 // deactivate it, the pipes don't work for some reason.
Bram Moolenaar05aafed2017-08-11 19:12:11 +02004948 vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE,
4949 &si, &pi, NULL, NULL);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004950
Bram Moolenaar6b707b42012-02-21 21:22:44 +01004951 if (p != cmd)
4952 vim_free(p);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004953
Bram Moolenaar0f873732019-12-05 20:28:46 +01004954 // Close our unused side of the pipes
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004955 CloseHandle(g_hChildStd_IN_Rd);
4956 CloseHandle(g_hChildStd_OUT_Wr);
4957
4958 if (options & SHELL_WRITE)
4959 {
Bram Moolenaar86f2cd52016-08-02 21:55:17 +02004960 HANDLE thread = (HANDLE)
Bram Moolenaar0f873732019-12-05 20:28:46 +01004961 _beginthreadex(NULL, // security attributes
4962 0, // default stack size
4963 sub_process_writer, // function to be executed
4964 g_hChildStd_IN_Wr, // parameter
4965 0, // creation flag, start immediately
4966 NULL); // we don't care about thread id
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004967 CloseHandle(thread);
4968 g_hChildStd_IN_Wr = NULL;
4969 }
4970
Bram Moolenaar0f873732019-12-05 20:28:46 +01004971 // Keep updating the window while waiting for the shell to finish.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004972 for (;;)
4973 {
4974 MSG msg;
4975
K.Takatab7057bd2022-01-21 11:37:07 +00004976 if (PeekMessageW(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004977 {
4978 TranslateMessage(&msg);
K.Takatab7057bd2022-01-21 11:37:07 +00004979 DispatchMessageW(&msg);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004980 }
4981
Bram Moolenaar0f873732019-12-05 20:28:46 +01004982 // write pipe information in the window
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02004983 if ((options & (SHELL_READ|SHELL_WRITE))
4984# ifdef FEAT_GUI
4985 || gui.in_use
4986# endif
4987 )
4988 {
4989 len = 0;
4990 if (!(options & SHELL_EXPAND)
4991 && ((options &
4992 (SHELL_READ|SHELL_WRITE|SHELL_COOKED))
4993 != (SHELL_READ|SHELL_WRITE|SHELL_COOKED)
4994# ifdef FEAT_GUI
4995 || gui.in_use
4996# endif
4997 )
4998 && (ta_len > 0 || noread_cnt > 4))
4999 {
5000 if (ta_len == 0)
5001 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005002 // Get extra characters when we don't have any. Reset the
5003 // counter and timer.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005004 noread_cnt = 0;
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005005 len = ui_inchar(ta_buf, BUFLEN, 10L, 0);
5006 }
5007 if (ta_len > 0 || len > 0)
5008 {
5009 /*
5010 * For pipes: Check for CTRL-C: send interrupt signal to
5011 * child. Check for CTRL-D: EOF, close pipe to child.
5012 */
5013 if (len == 1 && cmd != NULL)
5014 {
5015 if (ta_buf[ta_len] == Ctrl_C)
5016 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005017 // Learn what exit code is expected, for
5018 // now put 9 as SIGKILL
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005019 TerminateProcess(pi.hProcess, 9);
5020 }
5021 if (ta_buf[ta_len] == Ctrl_D)
5022 {
5023 CloseHandle(g_hChildStd_IN_Wr);
5024 g_hChildStd_IN_Wr = NULL;
5025 }
5026 }
5027
Bram Moolenaar2f7e1b82022-10-04 13:17:31 +01005028 len = term_replace_keycodes(ta_buf, ta_len, len);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005029
5030 /*
5031 * For pipes: echo the typed characters. For a pty this
5032 * does not seem to work.
5033 */
5034 for (i = ta_len; i < ta_len + len; ++i)
5035 {
5036 if (ta_buf[i] == '\n' || ta_buf[i] == '\b')
5037 msg_putchar(ta_buf[i]);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005038 else if (has_mbyte)
5039 {
5040 int l = (*mb_ptr2len)(ta_buf + i);
5041
5042 msg_outtrans_len(ta_buf + i, l);
5043 i += l - 1;
5044 }
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005045 else
5046 msg_outtrans_len(ta_buf + i, 1);
5047 }
5048 windgoto(msg_row, msg_col);
5049 out_flush();
5050
5051 ta_len += len;
5052
5053 /*
5054 * Write the characters to the child, unless EOF has been
5055 * typed for pipes. Write one character at a time, to
5056 * avoid losing too much typeahead. When writing buffer
5057 * lines, drop the typed characters (only check for
5058 * CTRL-C).
5059 */
5060 if (options & SHELL_WRITE)
5061 ta_len = 0;
5062 else if (g_hChildStd_IN_Wr != NULL)
5063 {
5064 WriteFile(g_hChildStd_IN_Wr, (char*)ta_buf,
5065 1, &len, NULL);
5066 // if we are typing in, we want to keep things reactive
5067 delay = 1;
5068 if (len > 0)
5069 {
5070 ta_len -= len;
5071 mch_memmove(ta_buf, ta_buf + len, ta_len);
5072 }
5073 }
5074 }
5075 }
5076 }
5077
5078 if (ta_len)
5079 ui_inchar_undo(ta_buf, ta_len);
5080
5081 if (WaitForSingleObject(pi.hProcess, delay) != WAIT_TIMEOUT)
5082 {
Bram Moolenaar2eba1822011-08-27 15:10:04 +02005083 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005084 break;
5085 }
5086
5087 ++noread_cnt;
Bram Moolenaar2eba1822011-08-27 15:10:04 +02005088 dump_pipe(options, g_hChildStd_OUT_Rd, &ga, buffer, &buffer_off);
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005089
Bram Moolenaar0f873732019-12-05 20:28:46 +01005090 // We start waiting for a very short time and then increase it, so
5091 // that we respond quickly when the process is quick, and don't
5092 // consume too much overhead when it's slow.
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005093 if (delay < 50)
5094 delay += 10;
5095 }
5096
Bram Moolenaar0f873732019-12-05 20:28:46 +01005097 // Close the pipe
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005098 CloseHandle(g_hChildStd_OUT_Rd);
5099 if (g_hChildStd_IN_Wr != NULL)
5100 CloseHandle(g_hChildStd_IN_Wr);
5101
5102 WaitForSingleObject(pi.hProcess, INFINITE);
5103
Bram Moolenaar0f873732019-12-05 20:28:46 +01005104 // Get the command exit code
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005105 GetExitCodeProcess(pi.hProcess, &ret);
5106
5107 if (options & SHELL_READ)
5108 {
5109 if (ga.ga_len > 0)
5110 {
5111 append_ga_line(&ga);
Bram Moolenaar0f873732019-12-05 20:28:46 +01005112 // remember that the NL was missing
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005113 curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
5114 }
5115 else
5116 curbuf->b_no_eol_lnum = 0;
5117 ga_clear(&ga);
5118 }
5119
Bram Moolenaar0f873732019-12-05 20:28:46 +01005120 // Close the handles to the subprocess, so that it goes away
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005121 CloseHandle(pi.hThread);
5122 CloseHandle(pi.hProcess);
5123
5124 return ret;
5125}
5126
5127 static int
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005128mch_system_g(char *cmd, int options)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005129{
Bram Moolenaar0f873732019-12-05 20:28:46 +01005130 // if we can pipe and the shelltemp option is off
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005131 if (!p_stmp)
Bram Moolenaar4b9669f2011-07-07 16:20:52 +02005132 return mch_system_piped(cmd, options);
5133 else
5134 return mch_system_classic(cmd, options);
5135}
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005136#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005138#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01005139 static int
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02005140mch_system_c(char *cmd, int options UNUSED)
Bram Moolenaar910cffb2013-12-11 17:58:35 +01005141{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005142 int ret;
5143 WCHAR *wcmd;
Bram Moolenaar2efc44b2019-10-05 12:09:32 +02005144 char_u *buf;
5145 size_t len;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005146
Bram Moolenaar2efc44b2019-10-05 12:09:32 +02005147 // If the command starts and ends with double quotes, enclose the command
5148 // in parentheses.
5149 len = STRLEN(cmd);
5150 if (len >= 2 && cmd[0] == '"' && cmd[len - 1] == '"')
5151 {
5152 len += 3;
5153 buf = alloc(len);
5154 if (buf == NULL)
5155 return -1;
5156 vim_snprintf((char *)buf, len, "(%s)", cmd);
5157 wcmd = enc_to_utf16(buf, NULL);
5158 free(buf);
5159 }
5160 else
5161 wcmd = enc_to_utf16((char_u *)cmd, NULL);
5162
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005163 if (wcmd == NULL)
5164 return -1;
5165
5166 ret = _wsystem(wcmd);
5167 vim_free(wcmd);
5168 return ret;
Bram Moolenaar910cffb2013-12-11 17:58:35 +01005169}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005170
5171#endif
5172
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005173 static int
5174mch_system(char *cmd, int options)
5175{
5176#ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02005177 if (gui.in_use || gui.starting)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005178 return mch_system_g(cmd, options);
5179 else
5180 return mch_system_c(cmd, options);
5181#elif defined(FEAT_GUI_MSWIN)
5182 return mch_system_g(cmd, options);
5183#else
5184 return mch_system_c(cmd, options);
5185#endif
5186}
5187
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005188#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
5189/*
5190 * Use a terminal window to run a shell command in.
5191 */
5192 static int
5193mch_call_shell_terminal(
5194 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01005195 int options UNUSED) // SHELL_*, see vim.h
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005196{
5197 jobopt_T opt;
5198 char_u *newcmd = NULL;
5199 typval_T argvar[2];
5200 long_u cmdlen;
5201 int retval = -1;
5202 buf_T *buf;
Bram Moolenaarf9c38832018-06-19 19:59:20 +02005203 job_T *job;
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005204 aco_save_T aco;
Bram Moolenaar0f873732019-12-05 20:28:46 +01005205 oparg_T oa; // operator arguments
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005206
Bram Moolenaar42f652f2018-03-19 21:44:37 +01005207 if (cmd == NULL)
5208 cmdlen = STRLEN(p_sh) + 1;
5209 else
5210 cmdlen = STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10;
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02005211 newcmd = alloc(cmdlen);
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005212 if (newcmd == NULL)
5213 return 255;
Bram Moolenaar42f652f2018-03-19 21:44:37 +01005214 if (cmd == NULL)
5215 {
5216 STRCPY(newcmd, p_sh);
5217 ch_log(NULL, "starting terminal to run a shell");
5218 }
5219 else
5220 {
5221 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s", p_sh, p_shcf, cmd);
5222 ch_log(NULL, "starting terminal for system command '%s'", cmd);
5223 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005224
5225 init_job_options(&opt);
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005226
5227 argvar[0].v_type = VAR_STRING;
5228 argvar[0].vval.v_string = newcmd;
5229 argvar[1].v_type = VAR_UNKNOWN;
5230 buf = term_start(argvar, NULL, &opt, TERM_START_SYSTEM);
Bram Moolenaar81c3c89a2018-03-20 11:41:44 +01005231 if (buf == NULL)
Bram Moolenaar9029b912019-03-21 19:58:00 +01005232 {
5233 vim_free(newcmd);
Bram Moolenaar81c3c89a2018-03-20 11:41:44 +01005234 return 255;
Bram Moolenaar9029b912019-03-21 19:58:00 +01005235 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005236
Bram Moolenaarf9c38832018-06-19 19:59:20 +02005237 job = term_getjob(buf->b_term);
5238 ++job->jv_refcount;
5239
Bram Moolenaar0f873732019-12-05 20:28:46 +01005240 // Find a window to make "buf" curbuf.
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005241 aucmd_prepbuf(&aco, buf);
Bram Moolenaare76062c2022-11-28 18:51:43 +00005242 if (curbuf == buf)
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005243 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00005244 // Only do this when a window was found for "buf".
5245 clear_oparg(&oa);
5246 while (term_use_loop())
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005247 {
Bram Moolenaare76062c2022-11-28 18:51:43 +00005248 if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active)
5249 {
5250 // If terminal_loop() returns OK we got a key that is handled
5251 // in Normal model. We don't do redrawing anyway.
5252 if (terminal_loop(TRUE) == OK)
5253 normal_cmd(&oa, TRUE);
5254 }
5255 else
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005256 normal_cmd(&oa, TRUE);
5257 }
Bram Moolenaare76062c2022-11-28 18:51:43 +00005258 retval = job->jv_exitval;
5259 ch_log(NULL, "system command finished");
5260
5261 job_unref(job);
5262
5263 // restore curwin/curbuf and a few other things
5264 aucmd_restbuf(&aco);
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005265 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005266
5267 wait_return(TRUE);
5268 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE);
5269
5270 vim_free(newcmd);
5271 return retval;
5272}
5273#endif
5274
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275/*
5276 * Either execute a command by calling the shell or start a new shell
5277 */
5278 int
5279mch_call_shell(
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005280 char_u *cmd,
Bram Moolenaar0f873732019-12-05 20:28:46 +01005281 int options) // SHELL_*, see vim.h
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282{
5283 int x = 0;
5284 int tmode = cur_tmode;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005285 WCHAR szShellTitle[512];
Bram Moolenaar799d6ab2014-10-16 16:16:37 +02005286
Bram Moolenaar4c5678f2022-11-30 18:12:19 +00005287#ifdef FEAT_EVAL
Bram Moolenaarc9a9a0a2022-04-12 15:09:23 +01005288 ch_log(NULL, "executing shell command: %s", cmd);
5289#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01005290 // Change the title to reflect that we are in a subshell.
K.Takataeeec2542021-06-02 13:28:16 +02005291 if (GetConsoleTitleW(szShellTitle, ARRAY_LENGTH(szShellTitle) - 4) > 0)
Bram Moolenaar1df52d72014-10-15 22:50:10 +02005292 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005293 if (cmd == NULL)
5294 wcscat(szShellTitle, L" :sh");
5295 else
Bram Moolenaar1df52d72014-10-15 22:50:10 +02005296 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005297 WCHAR *wn = enc_to_utf16((char_u *)cmd, NULL);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02005298
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005299 if (wn != NULL)
5300 {
5301 wcscat(szShellTitle, L" - !");
5302 if ((wcslen(szShellTitle) + wcslen(wn) <
K.Takataeeec2542021-06-02 13:28:16 +02005303 ARRAY_LENGTH(szShellTitle)))
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005304 wcscat(szShellTitle, wn);
5305 SetConsoleTitleW(szShellTitle);
5306 vim_free(wn);
Bram Moolenaar1df52d72014-10-15 22:50:10 +02005307 }
5308 }
5309 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005310
5311 out_flush();
5312
5313#ifdef MCH_WRITE_DUMP
5314 if (fdDump)
5315 {
5316 fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
5317 fflush(fdDump);
5318 }
5319#endif
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005320#if defined(FEAT_GUI) && defined(FEAT_TERMINAL)
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005321 // TODO: make the terminal window work with input or output redirected.
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005322 if (
5323# ifdef VIMDLL
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005324 gui.in_use &&
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005325# endif
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005326 vim_strchr(p_go, GO_TERMINAL) != NULL
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005327 && (options & (SHELL_FILTER|SHELL_DOOUT|SHELL_WRITE|SHELL_READ)) == 0)
5328 {
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005329 char_u *cmdbase = cmd;
5330
Bram Moolenaar4d5c1262019-09-20 17:20:02 +02005331 if (cmdbase != NULL)
5332 // Skip a leading quote and (.
5333 while (*cmdbase == '"' || *cmdbase == '(')
5334 ++cmdbase;
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005335
5336 // Check the command does not begin with "start "
Bram Moolenaar36e7a822019-11-13 21:49:24 +01005337 if (cmdbase == NULL || STRNICMP(cmdbase, "start", 5) != 0
5338 || !VIM_ISWHITE(cmdbase[5]))
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005339 {
5340 // Use a terminal window to run the command in.
5341 x = mch_call_shell_terminal(cmd, options);
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005342 resettitle();
Bram Moolenaar7c348bb2019-06-08 12:05:22 +02005343 return x;
5344 }
Bram Moolenaarf05fa372018-03-18 19:29:34 +01005345 }
5346#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347
5348 /*
5349 * Catch all deadly signals while running the external command, because a
5350 * CTRL-C, Ctrl-Break or illegal instruction might otherwise kill us.
5351 */
5352 signal(SIGINT, SIG_IGN);
5353#if defined(__GNUC__) && !defined(__MINGW32__)
5354 signal(SIGKILL, SIG_IGN);
5355#else
5356 signal(SIGBREAK, SIG_IGN);
5357#endif
5358 signal(SIGILL, SIG_IGN);
5359 signal(SIGFPE, SIG_IGN);
5360 signal(SIGSEGV, SIG_IGN);
5361 signal(SIGTERM, SIG_IGN);
5362 signal(SIGABRT, SIG_IGN);
5363
5364 if (options & SHELL_COOKED)
Bram Moolenaar0f873732019-12-05 20:28:46 +01005365 settmode(TMODE_COOK); // set to normal mode
Bram Moolenaar071d4272004-06-13 20:20:40 +00005366
5367 if (cmd == NULL)
5368 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005369 x = mch_system((char *)p_sh, options);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005370 }
5371 else
5372 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005373 // we use "command" or "cmd" to start the shell; slow but easy
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005374 char_u *newcmd = NULL;
5375 char_u *cmdbase = cmd;
5376 long_u cmdlen;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005377
Bram Moolenaar0f873732019-12-05 20:28:46 +01005378 // Skip a leading ", ( and "(.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005379 if (*cmdbase == '"' )
5380 ++cmdbase;
5381 if (*cmdbase == '(')
5382 ++cmdbase;
5383
Bram Moolenaar1c465442017-03-12 20:10:05 +01005384 if ((STRNICMP(cmdbase, "start", 5) == 0) && VIM_ISWHITE(cmdbase[5]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005385 {
5386 STARTUPINFO si;
5387 PROCESS_INFORMATION pi;
5388 DWORD flags = CREATE_NEW_CONSOLE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005389 INT n_show_cmd = SW_SHOWNORMAL;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005390 char_u *p;
5391
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005392 ZeroMemory(&si, sizeof(si));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005393 si.cb = sizeof(si);
5394 si.lpReserved = NULL;
5395 si.lpDesktop = NULL;
5396 si.lpTitle = NULL;
5397 si.dwFlags = 0;
5398 si.cbReserved2 = 0;
5399 si.lpReserved2 = NULL;
5400
5401 cmdbase = skipwhite(cmdbase + 5);
5402 if ((STRNICMP(cmdbase, "/min", 4) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01005403 && VIM_ISWHITE(cmdbase[4]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005404 {
5405 cmdbase = skipwhite(cmdbase + 4);
5406 si.dwFlags = STARTF_USESHOWWINDOW;
5407 si.wShowWindow = SW_SHOWMINNOACTIVE;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005408 n_show_cmd = SW_SHOWMINNOACTIVE;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005409 }
5410 else if ((STRNICMP(cmdbase, "/b", 2) == 0)
Bram Moolenaar1c465442017-03-12 20:10:05 +01005411 && VIM_ISWHITE(cmdbase[2]))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005412 {
5413 cmdbase = skipwhite(cmdbase + 2);
5414 flags = CREATE_NO_WINDOW;
5415 si.dwFlags = STARTF_USESTDHANDLES;
5416 si.hStdInput = CreateFile("\\\\.\\NUL", // File name
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005417 GENERIC_READ, // Access flags
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005418 0, // Share flags
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005419 NULL, // Security att.
5420 OPEN_EXISTING, // Open flags
5421 FILE_ATTRIBUTE_NORMAL, // File att.
5422 NULL); // Temp file
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005423 si.hStdOutput = si.hStdInput;
5424 si.hStdError = si.hStdInput;
5425 }
5426
Bram Moolenaar0f873732019-12-05 20:28:46 +01005427 // Remove a trailing ", ) and )" if they have a match
5428 // at the start of the command.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005429 if (cmdbase > cmd)
5430 {
5431 p = cmdbase + STRLEN(cmdbase);
5432 if (p > cmdbase && p[-1] == '"' && *cmd == '"')
5433 *--p = NUL;
5434 if (p > cmdbase && p[-1] == ')'
5435 && (*cmd =='(' || cmd[1] == '('))
5436 *--p = NUL;
5437 }
5438
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005439 newcmd = cmdbase;
5440 unescape_shellxquote(cmdbase, p_sxe);
5441
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005442 /*
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005443 * If creating new console, arguments are passed to the
5444 * 'cmd.exe' as-is. If it's not, arguments are not treated
5445 * correctly for current 'cmd.exe'. So unescape characters in
5446 * shellxescape except '|' for avoiding to be treated as
5447 * argument to them. Pass the arguments to sub-shell.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005448 */
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005449 if (flags != CREATE_NEW_CONSOLE)
5450 {
5451 char_u *subcmd;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005452 char_u *cmd_shell = mch_getenv("COMSPEC");
5453
5454 if (cmd_shell == NULL || *cmd_shell == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005455 cmd_shell = (char_u *)default_shell();
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005456
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005457 subcmd = vim_strsave_escaped_ext(cmdbase,
5458 (char_u *)"|", '^', FALSE);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005459 if (subcmd != NULL)
5460 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005461 // make "cmd.exe /c arguments"
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005462 cmdlen = STRLEN(cmd_shell) + STRLEN(subcmd) + 5;
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02005463 newcmd = alloc(cmdlen);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005464 if (newcmd != NULL)
5465 vim_snprintf((char *)newcmd, cmdlen, "%s /c %s",
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005466 cmd_shell, subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005467 else
5468 newcmd = cmdbase;
Bram Moolenaaree7d1002012-02-22 15:34:08 +01005469 vim_free(subcmd);
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005470 }
5471 }
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005472
5473 /*
5474 * Now, start the command as a process, so that it doesn't
5475 * inherit our handles which causes unpleasant dangling swap
5476 * files if we exit before the spawned process
5477 */
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005478 if (vim_create_process((char *)newcmd, FALSE, flags,
5479 &si, &pi, NULL, NULL))
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005480 x = 0;
Bram Moolenaarb2964f22017-03-21 19:29:26 +01005481 else if (vim_shell_execute((char *)newcmd, n_show_cmd)
5482 > (HINSTANCE)32)
5483 x = 0;
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005484 else
5485 {
5486 x = -1;
Bram Moolenaar4f974752019-02-17 17:44:42 +01005487#ifdef FEAT_GUI_MSWIN
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005488# ifdef VIMDLL
5489 if (gui.in_use)
5490# endif
Bram Moolenaarac78dd42022-01-02 19:25:26 +00005491 emsg(_(e_command_not_found));
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005492#endif
5493 }
Bram Moolenaarfb7df7b2012-02-22 13:07:05 +01005494
5495 if (newcmd != cmdbase)
5496 vim_free(newcmd);
5497
Bram Moolenaarfcc3f462014-01-24 19:55:37 +01005498 if (si.dwFlags == STARTF_USESTDHANDLES && si.hStdInput != NULL)
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005499 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005500 // Close the handle to \\.\NUL created above.
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005501 CloseHandle(si.hStdInput);
5502 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01005503 // Close the handles to the subprocess, so that it goes away
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005504 CloseHandle(pi.hThread);
5505 CloseHandle(pi.hProcess);
5506 }
5507 else
5508 {
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02005509 cmdlen =
Bram Moolenaar4f974752019-02-17 17:44:42 +01005510#ifdef FEAT_GUI_MSWIN
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02005511 ((gui.in_use || gui.starting) ?
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02005512 (!s_dont_use_vimrun && p_stmp ?
5513 STRLEN(vimrun_path) : STRLEN(p_sh) + STRLEN(p_shcf))
5514 : 0) +
Bram Moolenaar071d4272004-06-13 20:20:40 +00005515#endif
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02005516 STRLEN(p_sh) + STRLEN(p_shcf) + STRLEN(cmd) + 10;
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005517
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02005518 newcmd = alloc(cmdlen);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005519 if (newcmd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01005521#if defined(FEAT_GUI_MSWIN)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005522 if (
5523# ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02005524 (gui.in_use || gui.starting) &&
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005525# endif
5526 need_vimrun_warning)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527 {
Bram Moolenaar63e43442016-11-19 17:28:44 +01005528 char *msg = _("VIMRUN.EXE not found in your $PATH.\n"
5529 "External commands will not pause after completion.\n"
5530 "See :help win32-vimrun for more information.");
5531 char *title = _("Vim Warning");
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005532 WCHAR *wmsg = enc_to_utf16((char_u *)msg, NULL);
5533 WCHAR *wtitle = enc_to_utf16((char_u *)title, NULL);
Bram Moolenaar63e43442016-11-19 17:28:44 +01005534
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005535 if (wmsg != NULL && wtitle != NULL)
5536 MessageBoxW(NULL, wmsg, wtitle, MB_ICONWARNING);
5537 vim_free(wmsg);
5538 vim_free(wtitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 need_vimrun_warning = FALSE;
5540 }
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005541 if (
5542# ifdef VIMDLL
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02005543 (gui.in_use || gui.starting) &&
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005544# endif
5545 !s_dont_use_vimrun && p_stmp)
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02005546 // Use vimrun to execute the command. It opens a console
5547 // window, which can be closed without killing Vim.
Bram Moolenaarcc448b32010-07-14 16:52:17 +02005548 vim_snprintf((char *)newcmd, cmdlen, "%s%s%s %s %s",
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549 vimrun_path,
5550 (msg_silent != 0 || (options & SHELL_DOOUT))
5551 ? "-s " : "",
5552 p_sh, p_shcf, cmd);
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02005553 else if (
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02005554# ifdef VIMDLL
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02005555 (gui.in_use || gui.starting) &&
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02005556# endif
Bram Moolenaar0e6bfb92019-07-31 20:53:56 +02005557 s_dont_use_vimrun && STRCMP(p_shcf, "/c") == 0)
Bram Moolenaarfcc4d922019-05-24 13:32:36 +02005558 // workaround for the case that "vimrun" does not exist
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02005559 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s %s %s",
5560 p_sh, p_shcf, p_sh, p_shcf, cmd);
Bram Moolenaar98ffe4c2019-05-07 23:01:39 +02005561 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005562#endif
Bram Moolenaarcc448b32010-07-14 16:52:17 +02005563 vim_snprintf((char *)newcmd, cmdlen, "%s %s %s",
Bram Moolenaar0fde2902008-03-16 13:54:13 +00005564 p_sh, p_shcf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565 x = mch_system((char *)newcmd, options);
Bram Moolenaar6b707b42012-02-21 21:22:44 +01005566 vim_free(newcmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568 }
5569 }
5570
5571 if (tmode == TMODE_RAW)
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02005572 {
5573 // The shell may have messed with the mode, always set it.
5574 cur_tmode = TMODE_UNKNOWN;
Bram Moolenaar0f873732019-12-05 20:28:46 +01005575 settmode(TMODE_RAW); // set to raw mode
Bram Moolenaar3b1f18f2020-05-16 23:15:08 +02005576 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005577
Bram Moolenaar0f873732019-12-05 20:28:46 +01005578 // Print the return value, unless "vimrun" was used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005579 if (x != 0 && !(options & SHELL_SILENT) && !emsg_silent
Bram Moolenaar4f974752019-02-17 17:44:42 +01005580#if defined(FEAT_GUI_MSWIN)
Bram Moolenaar294d9bf2019-05-23 20:12:46 +02005581 && ((gui.in_use || gui.starting) ?
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005582 ((options & SHELL_DOOUT) || s_dont_use_vimrun || !p_stmp) : 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005583#endif
5584 )
5585 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005586 smsg(_("shell returned %d"), x);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005587 msg_putchar('\n');
5588 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005589 resettitle();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590
5591 signal(SIGINT, SIG_DFL);
5592#if defined(__GNUC__) && !defined(__MINGW32__)
5593 signal(SIGKILL, SIG_DFL);
5594#else
5595 signal(SIGBREAK, SIG_DFL);
5596#endif
5597 signal(SIGILL, SIG_DFL);
5598 signal(SIGFPE, SIG_DFL);
5599 signal(SIGSEGV, SIG_DFL);
5600 signal(SIGTERM, SIG_DFL);
5601 signal(SIGABRT, SIG_DFL);
5602
5603 return x;
5604}
5605
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01005606#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005607 static HANDLE
5608job_io_file_open(
Bram Moolenaar972c3b82017-01-12 21:44:49 +01005609 char_u *fname,
5610 DWORD dwDesiredAccess,
5611 DWORD dwShareMode,
5612 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
5613 DWORD dwCreationDisposition,
5614 DWORD dwFlagsAndAttributes)
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005615{
5616 HANDLE h;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005617 WCHAR *wn;
Bram Moolenaara12a1612019-01-24 16:39:02 +01005618
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005619 wn = enc_to_utf16(fname, NULL);
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005620 if (wn == NULL)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02005621 return INVALID_HANDLE_VALUE;
5622
5623 h = CreateFileW(wn, dwDesiredAccess, dwShareMode,
5624 lpSecurityAttributes, dwCreationDisposition,
5625 dwFlagsAndAttributes, NULL);
5626 vim_free(wn);
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005627 return h;
5628}
5629
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005630/*
5631 * Turn the dictionary "env" into a NUL separated list that can be used as the
5632 * environment argument of vim_create_process().
5633 */
Bram Moolenaarba6febd2017-10-30 21:56:23 +01005634 void
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005635win32_build_env(dict_T *env, garray_T *gap, int is_terminal)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005636{
5637 hashitem_T *hi;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005638 long_u todo = env != NULL ? env->dv_hashtab.ht_used : 0;
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005639 LPVOID base = GetEnvironmentStringsW();
5640
Bram Moolenaar0f873732019-12-05 20:28:46 +01005641 // for last \0
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005642 if (ga_grow(gap, 1) == FAIL)
5643 return;
5644
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005645 if (env != NULL)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005646 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005647 for (hi = env->dv_hashtab.ht_array; todo > 0; ++hi)
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005648 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005649 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005650 {
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005651 typval_T *item = &dict_lookup(hi)->di_tv;
5652 WCHAR *wkey = enc_to_utf16((char_u *)hi->hi_key, NULL);
Bram Moolenaard155d7a2018-12-21 16:04:21 +01005653 WCHAR *wval = enc_to_utf16(tv_get_string(item), NULL);
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005654 --todo;
5655 if (wkey != NULL && wval != NULL)
5656 {
5657 size_t n;
5658 size_t lkey = wcslen(wkey);
5659 size_t lval = wcslen(wval);
Bram Moolenaar60104f12017-08-14 23:25:04 +02005660
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005661 if (ga_grow(gap, (int)(lkey + lval + 2)) != OK)
5662 continue;
5663 for (n = 0; n < lkey; n++)
5664 *((WCHAR*)gap->ga_data + gap->ga_len++) = wkey[n];
5665 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'=';
5666 for (n = 0; n < lval; n++)
5667 *((WCHAR*)gap->ga_data + gap->ga_len++) = wval[n];
5668 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5669 }
Bram Moolenaarbdace832019-03-02 10:13:42 +01005670 vim_free(wkey);
5671 vim_free(wval);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005672 }
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005673 }
5674 }
5675
Bram Moolenaar355757a2020-02-10 22:06:32 +01005676 if (base)
5677 {
5678 WCHAR *p = (WCHAR*) base;
5679
5680 // for last \0
5681 if (ga_grow(gap, 1) == FAIL)
5682 return;
5683
5684 while (*p != 0 || *(p + 1) != 0)
5685 {
5686 if (ga_grow(gap, 1) == OK)
5687 *((WCHAR*)gap->ga_data + gap->ga_len++) = *p;
5688 p++;
5689 }
5690 FreeEnvironmentStrings(base);
5691 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5692 }
5693
Bram Moolenaar493359e2018-06-12 20:25:52 +02005694# if defined(FEAT_CLIENTSERVER) || defined(FEAT_TERMINAL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005695 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005696# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005697 char_u *servername = get_vim_var_str(VV_SEND_SERVER);
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005698 size_t servername_len = STRLEN(servername);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005699# endif
5700# ifdef FEAT_TERMINAL
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005701 char_u *version = get_vim_var_str(VV_VERSION);
5702 size_t version_len = STRLEN(version);
Bram Moolenaar493359e2018-06-12 20:25:52 +02005703# endif
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005704 // size of "VIM_SERVERNAME=" and value,
5705 // plus "VIM_TERMINAL=" and value,
5706 // plus two terminating NULs
5707 size_t n = 0
Bram Moolenaar493359e2018-06-12 20:25:52 +02005708# ifdef FEAT_CLIENTSERVER
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005709 + 15 + servername_len
Bram Moolenaar493359e2018-06-12 20:25:52 +02005710# endif
5711# ifdef FEAT_TERMINAL
5712 + 13 + version_len + 2
5713# endif
5714 ;
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005715
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005716 if (ga_grow(gap, (int)n) == OK)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005717 {
Bram Moolenaar493359e2018-06-12 20:25:52 +02005718# ifdef FEAT_CLIENTSERVER
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005719 for (n = 0; n < 15; n++)
5720 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5721 (WCHAR)"VIM_SERVERNAME="[n];
Bram Moolenaard7a137f2018-06-12 18:05:24 +02005722 for (n = 0; n < servername_len; n++)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005723 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5724 (WCHAR)servername[n];
5725 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
Bram Moolenaar493359e2018-06-12 20:25:52 +02005726# endif
5727# ifdef FEAT_TERMINAL
5728 if (is_terminal)
5729 {
5730 for (n = 0; n < 13; n++)
5731 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5732 (WCHAR)"VIM_TERMINAL="[n];
5733 for (n = 0; n < version_len; n++)
5734 *((WCHAR*)gap->ga_data + gap->ga_len++) =
5735 (WCHAR)version[n];
5736 *((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
5737 }
5738# endif
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005739 }
5740 }
Bram Moolenaar79c6b512018-06-12 21:11:12 +02005741# endif
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005742}
5743
Bram Moolenaarb091f302019-01-19 14:37:00 +01005744/*
5745 * Create a pair of pipes.
5746 * Return TRUE for success, FALSE for failure.
5747 */
5748 static BOOL
5749create_pipe_pair(HANDLE handles[2])
5750{
5751 static LONG s;
5752 char name[64];
5753 SECURITY_ATTRIBUTES sa;
5754
5755 sprintf(name, "\\\\?\\pipe\\vim-%08lx-%08lx",
5756 GetCurrentProcessId(),
5757 InterlockedIncrement(&s));
5758
5759 // Create named pipe. Max size of named pipe is 65535.
5760 handles[1] = CreateNamedPipe(
5761 name,
5762 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
5763 PIPE_TYPE_BYTE | PIPE_NOWAIT,
Bram Moolenaar24058382019-01-24 23:11:49 +01005764 1, MAX_NAMED_PIPE_SIZE, 0, 0, NULL);
Bram Moolenaarb091f302019-01-19 14:37:00 +01005765
5766 if (handles[1] == INVALID_HANDLE_VALUE)
5767 return FALSE;
5768
5769 sa.nLength = sizeof(sa);
5770 sa.bInheritHandle = TRUE;
5771 sa.lpSecurityDescriptor = NULL;
5772
5773 handles[0] = CreateFile(name,
5774 FILE_GENERIC_READ,
5775 FILE_SHARE_READ, &sa,
5776 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
5777
5778 if (handles[0] == INVALID_HANDLE_VALUE)
5779 {
Bram Moolenaar6982f422019-02-16 16:48:01 +01005780 CloseHandle(handles[1]);
Bram Moolenaarb091f302019-01-19 14:37:00 +01005781 return FALSE;
5782 }
5783
5784 return TRUE;
5785}
5786
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005787 void
Bram Moolenaar5a1feb82017-07-22 18:04:08 +02005788mch_job_start(char *cmd, job_T *job, jobopt_T *options)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005789{
5790 STARTUPINFO si;
5791 PROCESS_INFORMATION pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005792 HANDLE jo;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005793 SECURITY_ATTRIBUTES saAttr;
5794 channel_T *channel = NULL;
Bram Moolenaard8070362016-02-15 21:56:54 +01005795 HANDLE ifd[2];
5796 HANDLE ofd[2];
5797 HANDLE efd[2];
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005798 garray_T ga;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005799
5800 int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
5801 int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
5802 int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL;
5803 int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE;
5804 int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE;
5805 int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
5806 int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT;
5807
5808 if (use_out_for_err && use_null_for_out)
5809 use_null_for_err = TRUE;
Bram Moolenaard8070362016-02-15 21:56:54 +01005810
5811 ifd[0] = INVALID_HANDLE_VALUE;
5812 ifd[1] = INVALID_HANDLE_VALUE;
5813 ofd[0] = INVALID_HANDLE_VALUE;
5814 ofd[1] = INVALID_HANDLE_VALUE;
5815 efd[0] = INVALID_HANDLE_VALUE;
5816 efd[1] = INVALID_HANDLE_VALUE;
Bram Moolenaar04935fb2022-01-08 16:19:22 +00005817 ga_init2(&ga, sizeof(wchar_t), 500);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005818
Bram Moolenaar14207f42016-10-27 21:13:10 +02005819 jo = CreateJobObject(NULL, NULL);
5820 if (jo == NULL)
5821 {
5822 job->jv_status = JOB_FAILED;
5823 goto failed;
5824 }
5825
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005826 if (options->jo_env != NULL)
Bram Moolenaar52dbb5e2017-11-21 18:11:27 +01005827 win32_build_env(options->jo_env, &ga, FALSE);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005828
Bram Moolenaar76467df2016-02-12 19:30:26 +01005829 ZeroMemory(&pi, sizeof(pi));
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005830 ZeroMemory(&si, sizeof(si));
5831 si.cb = sizeof(si);
Bram Moolenaard8070362016-02-15 21:56:54 +01005832 si.dwFlags |= STARTF_USESHOWWINDOW;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005833 si.wShowWindow = SW_HIDE;
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005834
Bram Moolenaard8070362016-02-15 21:56:54 +01005835 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
5836 saAttr.bInheritHandle = TRUE;
5837 saAttr.lpSecurityDescriptor = NULL;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005838
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005839 if (use_file_for_in)
5840 {
5841 char_u *fname = options->jo_io_name[PART_IN];
5842
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005843 ifd[0] = job_io_file_open(fname, GENERIC_READ,
5844 FILE_SHARE_READ | FILE_SHARE_WRITE,
5845 &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL);
5846 if (ifd[0] == INVALID_HANDLE_VALUE)
Bram Moolenaar94d01912016-03-08 13:48:51 +01005847 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005848 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaar94d01912016-03-08 13:48:51 +01005849 goto failed;
5850 }
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005851 }
Bram Moolenaarb091f302019-01-19 14:37:00 +01005852 else if (!use_null_for_in
5853 && (!create_pipe_pair(ifd)
5854 || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005855 goto failed;
5856
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005857 if (use_file_for_out)
5858 {
5859 char_u *fname = options->jo_io_name[PART_OUT];
5860
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005861 ofd[1] = job_io_file_open(fname, GENERIC_WRITE,
5862 FILE_SHARE_READ | FILE_SHARE_WRITE,
5863 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5864 if (ofd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005865 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005866 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005867 goto failed;
5868 }
5869 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005870 else if (!use_null_for_out &&
5871 (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005872 || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaarb69fccf2016-03-06 23:06:25 +01005873 goto failed;
5874
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005875 if (use_file_for_err)
5876 {
5877 char_u *fname = options->jo_io_name[PART_ERR];
5878
Bram Moolenaar7bffaa92016-03-10 21:46:03 +01005879 efd[1] = job_io_file_open(fname, GENERIC_WRITE,
5880 FILE_SHARE_READ | FILE_SHARE_WRITE,
5881 &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL);
5882 if (efd[1] == INVALID_HANDLE_VALUE)
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005883 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005884 semsg(_(e_cant_open_file_str), fname);
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005885 goto failed;
5886 }
5887 }
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005888 else if (!use_out_for_err && !use_null_for_err &&
5889 (!CreatePipe(&efd[0], &efd[1], &saAttr, 0)
Bram Moolenaarcea912a2016-10-12 14:20:24 +02005890 || !SetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0)))
Bram Moolenaard8070362016-02-15 21:56:54 +01005891 goto failed;
Bram Moolenaar13d6fb12016-03-08 18:40:52 +01005892
Bram Moolenaard8070362016-02-15 21:56:54 +01005893 si.dwFlags |= STARTF_USESTDHANDLES;
5894 si.hStdInput = ifd[0];
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005895 si.hStdOutput = ofd[1];
5896 si.hStdError = use_out_for_err ? ofd[1] : efd[1];
5897
5898 if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
5899 {
Bram Moolenaarde279892016-03-11 22:19:44 +01005900 if (options->jo_set & JO_CHANNEL)
5901 {
5902 channel = options->jo_channel;
5903 if (channel != NULL)
5904 ++channel->ch_refcount;
5905 }
5906 else
5907 channel = add_channel();
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005908 if (channel == NULL)
5909 goto failed;
5910 }
Bram Moolenaard8070362016-02-15 21:56:54 +01005911
5912 if (!vim_create_process(cmd, TRUE,
Bram Moolenaar14207f42016-10-27 21:13:10 +02005913 CREATE_SUSPENDED |
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005914 CREATE_DEFAULT_ERROR_MODE |
5915 CREATE_NEW_PROCESS_GROUP |
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005916 CREATE_UNICODE_ENVIRONMENT |
Bram Moolenaar76467df2016-02-12 19:30:26 +01005917 CREATE_NEW_CONSOLE,
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005918 &si, &pi,
5919 ga.ga_data,
5920 (char *)options->jo_cwd))
Bram Moolenaar76467df2016-02-12 19:30:26 +01005921 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02005922 CloseHandle(jo);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005923 job->jv_status = JOB_FAILED;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005924 goto failed;
Bram Moolenaar76467df2016-02-12 19:30:26 +01005925 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005926
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005927 ga_clear(&ga);
5928
Bram Moolenaar14207f42016-10-27 21:13:10 +02005929 if (!AssignProcessToJobObject(jo, pi.hProcess))
5930 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01005931 // if failing, switch the way to terminate
5932 // process with TerminateProcess.
Bram Moolenaar14207f42016-10-27 21:13:10 +02005933 CloseHandle(jo);
5934 jo = NULL;
5935 }
5936 ResumeThread(pi.hThread);
Bram Moolenaar75578a32016-03-10 16:33:31 +01005937 CloseHandle(pi.hThread);
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005938 job->jv_proc_info = pi;
Bram Moolenaar14207f42016-10-27 21:13:10 +02005939 job->jv_job_object = jo;
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005940 job->jv_status = JOB_STARTED;
5941
Bram Moolenaar641ad6c2016-09-01 18:32:11 +02005942 CloseHandle(ifd[0]);
5943 CloseHandle(ofd[1]);
5944 if (!use_out_for_err && !use_null_for_err)
Bram Moolenaarc25558b2016-03-03 21:02:23 +01005945 CloseHandle(efd[1]);
Bram Moolenaard8070362016-02-15 21:56:54 +01005946
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005947 job->jv_channel = channel;
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005948 if (channel != NULL)
5949 {
5950 channel_set_pipes(channel,
5951 use_file_for_in || use_null_for_in
5952 ? INVALID_FD : (sock_T)ifd[1],
5953 use_file_for_out || use_null_for_out
5954 ? INVALID_FD : (sock_T)ofd[0],
5955 use_out_for_err || use_file_for_err || use_null_for_err
5956 ? INVALID_FD : (sock_T)efd[0]);
5957 channel_set_job(channel, job, options);
Bram Moolenaard5d3d302016-03-09 20:54:51 +01005958 }
Bram Moolenaar7b3ca762016-02-14 19:13:43 +01005959 return;
5960
5961failed:
Bram Moolenaard8070362016-02-15 21:56:54 +01005962 CloseHandle(ifd[0]);
5963 CloseHandle(ofd[0]);
5964 CloseHandle(efd[0]);
5965 CloseHandle(ifd[1]);
5966 CloseHandle(ofd[1]);
5967 CloseHandle(efd[1]);
Bram Moolenaarde279892016-03-11 22:19:44 +01005968 channel_unref(channel);
Bram Moolenaar05aafed2017-08-11 19:12:11 +02005969 ga_clear(&ga);
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005970}
5971
5972 char *
5973mch_job_status(job_T *job)
5974{
5975 DWORD dwExitCode = 0;
5976
Bram Moolenaar76467df2016-02-12 19:30:26 +01005977 if (!GetExitCodeProcess(job->jv_proc_info.hProcess, &dwExitCode)
5978 || dwExitCode != STILL_ACTIVE)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005979 {
Bram Moolenaareab089d2016-02-21 19:32:02 +01005980 job->jv_exitval = (int)dwExitCode;
Bram Moolenaar7df915d2016-11-17 17:25:32 +01005981 if (job->jv_status < JOB_ENDED)
Bram Moolenaar97792de2016-10-15 18:36:49 +02005982 {
5983 ch_log(job->jv_channel, "Job ended");
5984 job->jv_status = JOB_ENDED;
5985 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01005986 return "dead";
5987 }
5988 return "run";
5989}
5990
Bram Moolenaar97792de2016-10-15 18:36:49 +02005991 job_T *
5992mch_detect_ended_job(job_T *job_list)
5993{
5994 HANDLE jobHandles[MAXIMUM_WAIT_OBJECTS];
5995 job_T *jobArray[MAXIMUM_WAIT_OBJECTS];
5996 job_T *job = job_list;
5997
5998 while (job != NULL)
5999 {
6000 DWORD n;
6001 DWORD result;
6002
6003 for (n = 0; n < MAXIMUM_WAIT_OBJECTS
6004 && job != NULL; job = job->jv_next)
6005 {
6006 if (job->jv_status == JOB_STARTED)
6007 {
6008 jobHandles[n] = job->jv_proc_info.hProcess;
6009 jobArray[n] = job;
6010 ++n;
6011 }
6012 }
6013 if (n == 0)
6014 continue;
6015 result = WaitForMultipleObjects(n, jobHandles, FALSE, 0);
6016 if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n)
6017 {
6018 job_T *wait_job = jobArray[result - WAIT_OBJECT_0];
6019
6020 if (STRCMP(mch_job_status(wait_job), "dead") == 0)
6021 return wait_job;
6022 }
6023 }
6024 return NULL;
6025}
6026
Bram Moolenaarfb630902016-10-29 14:55:00 +02006027 static BOOL
6028terminate_all(HANDLE process, int code)
6029{
6030 PROCESSENTRY32 pe;
6031 HANDLE h = INVALID_HANDLE_VALUE;
6032 DWORD pid = GetProcessId(process);
6033
6034 if (pid != 0)
6035 {
6036 h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
6037 if (h != INVALID_HANDLE_VALUE)
6038 {
6039 pe.dwSize = sizeof(PROCESSENTRY32);
6040 if (!Process32First(h, &pe))
6041 goto theend;
6042
6043 do
6044 {
6045 if (pe.th32ParentProcessID == pid)
6046 {
6047 HANDLE ph = OpenProcess(
6048 PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
6049 if (ph != NULL)
6050 {
6051 terminate_all(ph, code);
6052 CloseHandle(ph);
6053 }
6054 }
6055 } while (Process32Next(h, &pe));
6056
6057 CloseHandle(h);
6058 }
6059 }
6060
6061theend:
6062 return TerminateProcess(process, code);
6063}
6064
6065/*
6066 * Send a (deadly) signal to "job".
6067 * Return FAIL if it didn't work.
6068 */
Bram Moolenaar942d6b22016-02-07 19:57:16 +01006069 int
Bram Moolenaar2d33e902017-08-11 16:31:54 +02006070mch_signal_job(job_T *job, char_u *how)
Bram Moolenaar942d6b22016-02-07 19:57:16 +01006071{
Bram Moolenaar923d9262016-02-25 20:56:01 +01006072 int ret;
Bram Moolenaar76467df2016-02-12 19:30:26 +01006073
Bram Moolenaar923d9262016-02-25 20:56:01 +01006074 if (STRCMP(how, "term") == 0 || STRCMP(how, "kill") == 0 || *how == NUL)
Bram Moolenaar76467df2016-02-12 19:30:26 +01006075 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01006076 // deadly signal
Bram Moolenaar14207f42016-10-27 21:13:10 +02006077 if (job->jv_job_object != NULL)
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01006078 {
6079 if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe)
6080 job->jv_channel->ch_killing = TRUE;
K.Takata135e1522022-01-29 15:27:58 +00006081 return TerminateJobObject(job->jv_job_object, (UINT)-1) ? OK : FAIL;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01006082 }
Bram Moolenaarb3e195c2020-02-10 21:32:19 +01006083 return terminate_all(job->jv_proc_info.hProcess, -1) ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01006084 }
6085
6086 if (!AttachConsole(job->jv_proc_info.dwProcessId))
6087 return FAIL;
6088 ret = GenerateConsoleCtrlEvent(
Bram Moolenaar923d9262016-02-25 20:56:01 +01006089 STRCMP(how, "int") == 0 ? CTRL_C_EVENT : CTRL_BREAK_EVENT,
6090 job->jv_proc_info.dwProcessId)
6091 ? OK : FAIL;
Bram Moolenaar76467df2016-02-12 19:30:26 +01006092 FreeConsole();
6093 return ret;
6094}
6095
6096/*
6097 * Clear the data related to "job".
6098 */
6099 void
6100mch_clear_job(job_T *job)
6101{
6102 if (job->jv_status != JOB_FAILED)
6103 {
Bram Moolenaar14207f42016-10-27 21:13:10 +02006104 if (job->jv_job_object != NULL)
6105 CloseHandle(job->jv_job_object);
Bram Moolenaar76467df2016-02-12 19:30:26 +01006106 CloseHandle(job->jv_proc_info.hProcess);
6107 }
Bram Moolenaar942d6b22016-02-07 19:57:16 +01006108}
6109#endif
6110
Bram Moolenaar071d4272004-06-13 20:20:40 +00006111
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006112#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006113
6114/*
6115 * Start termcap mode
6116 */
6117 static void
6118termcap_mode_start(void)
6119{
6120 DWORD cmodein;
6121
6122 if (g_fTermcapMode)
6123 return;
6124
Christopher Plewright1140b512022-11-12 18:46:05 +00006125 // VTP uses alternate screen buffer.
6126 // Switch to a new alternate screen buffer.
Christopher Plewrightc8b126d2022-12-22 13:45:23 +00006127 // But, not if running in a nested terminal
6128 if (use_alternate_screen_buffer)
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02006129 vtp_printf("\033[?1049h");
6130
Bram Moolenaar071d4272004-06-13 20:20:40 +00006131 SaveConsoleBuffer(&g_cbNonTermcap);
6132
6133 if (g_cbTermcap.IsValid)
6134 {
6135 /*
6136 * We've been in termcap mode before. Restore certain screen
6137 * characteristics, including the buffer size and the window
6138 * size. Since we will be redrawing the screen, we don't need
6139 * to restore the actual contents of the buffer.
6140 */
6141 RestoreConsoleBuffer(&g_cbTermcap, FALSE);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006142 reset_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006143 SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow);
6144 Rows = g_cbTermcap.Info.dwSize.Y;
6145 Columns = g_cbTermcap.Info.dwSize.X;
6146 }
6147 else
6148 {
6149 /*
6150 * This is our first time entering termcap mode. Clear the console
6151 * screen buffer, and resize the buffer to match the current window
6152 * size. We will use this as the size of our editing environment.
6153 */
6154 ClearConsoleBuffer(g_attrCurrent);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006155 set_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006156 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
6157 }
6158
Bram Moolenaar071d4272004-06-13 20:20:40 +00006159 resettitle();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006160
6161 GetConsoleMode(g_hConIn, &cmodein);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006162 if (g_fMouseActive)
Wez Furlong6ef5ab52021-05-30 19:29:41 +02006163 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006164 cmodein |= ENABLE_MOUSE_INPUT;
Wez Furlong6ef5ab52021-05-30 19:29:41 +02006165 cmodein &= ~ENABLE_QUICK_EDIT_MODE;
6166 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006167 else
Wez Furlong6ef5ab52021-05-30 19:29:41 +02006168 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006169 cmodein &= ~ENABLE_MOUSE_INPUT;
Wez Furlong6ef5ab52021-05-30 19:29:41 +02006170 cmodein |= g_cmodein & ENABLE_QUICK_EDIT_MODE;
6171 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006172 cmodein |= ENABLE_WINDOW_INPUT;
Wez Furlong6ef5ab52021-05-30 19:29:41 +02006173 SetConsoleMode(g_hConIn, cmodein | ENABLE_EXTENDED_FLAGS);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006174
6175 redraw_later_clear();
6176 g_fTermcapMode = TRUE;
6177}
6178
6179
6180/*
6181 * End termcap mode
6182 */
6183 static void
6184termcap_mode_end(void)
6185{
6186 DWORD cmodein;
6187 ConsoleBuffer *cb;
6188 COORD coord;
6189 DWORD dwDummy;
6190
6191 if (!g_fTermcapMode)
6192 return;
6193
6194 SaveConsoleBuffer(&g_cbTermcap);
6195
6196 GetConsoleMode(g_hConIn, &cmodein);
6197 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
Wez Furlong6ef5ab52021-05-30 19:29:41 +02006198 cmodein |= g_cmodein & ENABLE_QUICK_EDIT_MODE;
6199 SetConsoleMode(g_hConIn, cmodein | ENABLE_EXTENDED_FLAGS);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006200
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006201# ifdef FEAT_RESTORE_ORIG_SCREEN
Bram Moolenaar4c0aac52015-10-30 16:46:55 +01006202 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006203# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006204 cb = &g_cbNonTermcap;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006205# endif
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02006206 RestoreConsoleBuffer(cb, p_rs);
Bram Moolenaardf543822020-01-30 11:53:59 +01006207 restore_console_color_rgb();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006208
Christopher Plewright1140b512022-11-12 18:46:05 +00006209 // VTP uses alternate screen buffer.
6210 // Switch back to main screen buffer.
Christopher Plewrightc8b126d2022-12-22 13:45:23 +00006211 if (exiting && use_alternate_screen_buffer)
Christopher Plewright1140b512022-11-12 18:46:05 +00006212 vtp_printf("\033[?1049l");
6213
6214 if (!USE_WT && (p_rs || exiting))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006215 {
6216 /*
6217 * Clear anything that happens to be on the current line.
6218 */
6219 coord.X = 0;
6220 coord.Y = (SHORT) (p_rs ? cb->Info.dwCursorPosition.Y : (Rows - 1));
Christopher Plewright1140b512022-11-12 18:46:05 +00006221 if (!vtp_working)
6222 FillConsoleOutputCharacter(g_hConOut, ' ',
6223 cb->Info.dwSize.X, coord, &dwDummy);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006224 /*
6225 * The following is just for aesthetics. If we are exiting without
6226 * restoring the screen, then we want to have a prompt string
6227 * appear at the bottom line. However, the command interpreter
6228 * seems to always advance the cursor one line before displaying
6229 * the prompt string, which causes the screen to scroll. To
6230 * counter this, move the cursor up one line before exiting.
6231 */
6232 if (exiting && !p_rs)
6233 coord.Y--;
6234 /*
6235 * Position the cursor at the leftmost column of the desired row.
6236 */
Bram Moolenaar81ccbf12020-04-15 21:05:30 +02006237 SetConsoleCursorPosition(g_hConOut, coord);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006238 }
Christopher Plewright1140b512022-11-12 18:46:05 +00006239 SetConsoleCursorInfo(g_hConOut, &g_cci);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006240 g_fTermcapMode = FALSE;
6241}
Christopher Plewright38804d62022-11-09 23:55:52 +00006242#endif // !FEAT_GUI_MSWIN || VIMDLL
Bram Moolenaar071d4272004-06-13 20:20:40 +00006243
6244
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006245#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006246 void
6247mch_write(
Bram Moolenaar1266d672017-02-01 13:43:36 +01006248 char_u *s UNUSED,
6249 int len UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006250{
Bram Moolenaar0f873732019-12-05 20:28:46 +01006251 // never used
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252}
6253
6254#else
6255
6256/*
6257 * clear `n' chars, starting from `coord'
6258 */
6259 static void
6260clear_chars(
6261 COORD coord,
6262 DWORD n)
6263{
Christopher Plewright38804d62022-11-09 23:55:52 +00006264 if (!vtp_working)
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006265 {
6266 DWORD dwDummy;
6267
6268 FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
6269 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord,
6270 &dwDummy);
6271 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006272 else
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02006273 {
6274 set_console_color_rgb();
6275 gotoxy(coord.X + 1, coord.Y + 1);
6276 vtp_printf("\033[%dX", n);
6277 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006278}
6279
6280
6281/*
6282 * Clear the screen
6283 */
6284 static void
6285clear_screen(void)
6286{
6287 g_coord.X = g_coord.Y = 0;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006288
Christopher Plewright38804d62022-11-09 23:55:52 +00006289 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006290 clear_chars(g_coord, Rows * Columns);
6291 else
6292 {
6293 set_console_color_rgb();
6294 gotoxy(1, 1);
6295 vtp_printf("\033[2J");
6296 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006297}
6298
6299
6300/*
6301 * Clear to end of display
6302 */
6303 static void
6304clear_to_end_of_display(void)
6305{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006306 COORD save = g_coord;
6307
Christopher Plewright38804d62022-11-09 23:55:52 +00006308 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006309 clear_chars(g_coord, (Rows - g_coord.Y - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006310 * Columns + (Columns - g_coord.X));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006311 else
6312 {
6313 set_console_color_rgb();
6314 gotoxy(g_coord.X + 1, g_coord.Y + 1);
6315 vtp_printf("\033[0J");
6316
6317 gotoxy(save.X + 1, save.Y + 1);
6318 g_coord = save;
6319 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320}
6321
6322
6323/*
6324 * Clear to end of line
6325 */
6326 static void
6327clear_to_end_of_line(void)
6328{
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006329 COORD save = g_coord;
6330
Christopher Plewright38804d62022-11-09 23:55:52 +00006331 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006332 clear_chars(g_coord, Columns - g_coord.X);
6333 else
6334 {
6335 set_console_color_rgb();
6336 gotoxy(g_coord.X + 1, g_coord.Y + 1);
6337 vtp_printf("\033[0K");
6338
6339 gotoxy(save.X + 1, save.Y + 1);
6340 g_coord = save;
6341 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006342}
6343
6344
6345/*
6346 * Scroll the scroll region up by `cLines' lines
6347 */
6348 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006349scroll(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350{
6351 COORD oldcoord = g_coord;
6352
6353 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
6354 delete_lines(cLines);
6355
6356 g_coord = oldcoord;
6357}
6358
6359
6360/*
6361 * Set the scroll region
6362 */
6363 static void
6364set_scroll_region(
6365 unsigned left,
6366 unsigned top,
6367 unsigned right,
6368 unsigned bottom)
6369{
6370 if (left >= right
6371 || top >= bottom
6372 || right > (unsigned) Columns - 1
6373 || bottom > (unsigned) Rows - 1)
6374 return;
6375
6376 g_srScrollRegion.Left = left;
6377 g_srScrollRegion.Top = top;
6378 g_srScrollRegion.Right = right;
6379 g_srScrollRegion.Bottom = bottom;
Bram Moolenaar6982f422019-02-16 16:48:01 +01006380}
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006381
Bram Moolenaar6982f422019-02-16 16:48:01 +01006382 static void
6383set_scroll_region_tb(
6384 unsigned top,
6385 unsigned bottom)
6386{
6387 if (top >= bottom || bottom > (unsigned)Rows - 1)
6388 return;
6389
6390 g_srScrollRegion.Top = top;
6391 g_srScrollRegion.Bottom = bottom;
6392}
6393
6394 static void
6395set_scroll_region_lr(
6396 unsigned left,
6397 unsigned right)
6398{
6399 if (left >= right || right > (unsigned)Columns - 1)
6400 return;
6401
6402 g_srScrollRegion.Left = left;
6403 g_srScrollRegion.Right = right;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006404}
6405
6406
6407/*
6408 * Insert `cLines' lines at the current cursor position
6409 */
6410 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006411insert_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006412{
Bram Moolenaar6982f422019-02-16 16:48:01 +01006413 SMALL_RECT source, clip;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006414 COORD dest;
6415 CHAR_INFO fill;
6416
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006417 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
6418
Bram Moolenaar6982f422019-02-16 16:48:01 +01006419 dest.X = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420 dest.Y = g_coord.Y + cLines;
6421
Bram Moolenaar6982f422019-02-16 16:48:01 +01006422 source.Left = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006423 source.Top = g_coord.Y;
6424 source.Right = g_srScrollRegion.Right;
6425 source.Bottom = g_srScrollRegion.Bottom - cLines;
6426
Bram Moolenaar6982f422019-02-16 16:48:01 +01006427 clip.Left = g_srScrollRegion.Left;
6428 clip.Top = g_coord.Y;
6429 clip.Right = g_srScrollRegion.Right;
6430 clip.Bottom = g_srScrollRegion.Bottom;
6431
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006432 fill.Char.AsciiChar = ' ';
Christopher Plewright38804d62022-11-09 23:55:52 +00006433 if (!(vtp_working
K.Takata161b6ac2022-11-14 15:31:07 +00006434# ifdef FEAT_TERMGUICOLORS
Christopher Plewright38804d62022-11-09 23:55:52 +00006435 && (p_tgc || t_colors >= 256)
K.Takata161b6ac2022-11-14 15:31:07 +00006436# endif
Christopher Plewright38804d62022-11-09 23:55:52 +00006437 ))
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006438 fill.Attributes = g_attrCurrent;
6439 else
6440 fill.Attributes = g_attrDefault;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006441
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006442 set_console_color_rgb();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006443
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006444 ScrollConsoleScreenBuffer(g_hConOut, &source, &clip, dest, &fill);
6445
Bram Moolenaar6982f422019-02-16 16:48:01 +01006446 // Here we have to deal with a win32 console flake: If the scroll
6447 // region looks like abc and we scroll c to a and fill with d we get
6448 // cbd... if we scroll block c one line at a time to a, we get cdd...
6449 // vim expects cdd consistently... So we have to deal with that
6450 // here... (this also occurs scrolling the same way in the other
6451 // direction).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006452
6453 if (source.Bottom < dest.Y)
6454 {
6455 COORD coord;
Bram Moolenaar6982f422019-02-16 16:48:01 +01006456 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006457
Bram Moolenaar6982f422019-02-16 16:48:01 +01006458 coord.X = source.Left;
6459 for (i = clip.Top; i < dest.Y; ++i)
6460 {
6461 coord.Y = i;
6462 clear_chars(coord, source.Right - source.Left + 1);
6463 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464 }
Bram Moolenaar06b7b582020-05-30 17:49:25 +02006465
Christopher Plewright38804d62022-11-09 23:55:52 +00006466 if (vtp_working)
Bram Moolenaar06b7b582020-05-30 17:49:25 +02006467 {
6468 COORD coord;
6469 int i;
6470
6471 coord.X = source.Left;
6472 for (i = source.Top; i < dest.Y; ++i)
6473 {
6474 coord.Y = i;
6475 clear_chars(coord, source.Right - source.Left + 1);
6476 }
6477 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006478}
6479
6480
6481/*
6482 * Delete `cLines' lines at the current cursor position
6483 */
6484 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006485delete_lines(unsigned cLines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006486{
Bram Moolenaar6982f422019-02-16 16:48:01 +01006487 SMALL_RECT source, clip;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006488 COORD dest;
6489 CHAR_INFO fill;
6490 int nb;
6491
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006492 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
6493
Bram Moolenaar6982f422019-02-16 16:48:01 +01006494 dest.X = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006495 dest.Y = g_coord.Y;
6496
Bram Moolenaar6982f422019-02-16 16:48:01 +01006497 source.Left = g_srScrollRegion.Left;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006498 source.Top = g_coord.Y + cLines;
6499 source.Right = g_srScrollRegion.Right;
6500 source.Bottom = g_srScrollRegion.Bottom;
6501
Bram Moolenaar6982f422019-02-16 16:48:01 +01006502 clip.Left = g_srScrollRegion.Left;
6503 clip.Top = g_coord.Y;
6504 clip.Right = g_srScrollRegion.Right;
6505 clip.Bottom = g_srScrollRegion.Bottom;
6506
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006507 fill.Char.AsciiChar = ' ';
Christopher Plewright38804d62022-11-09 23:55:52 +00006508 if (!vtp_working)
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006509 fill.Attributes = g_attrCurrent;
6510 else
6511 fill.Attributes = g_attrDefault;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006513 set_console_color_rgb();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006514
Bram Moolenaar3b5fef62019-03-17 14:54:53 +01006515 ScrollConsoleScreenBuffer(g_hConOut, &source, &clip, dest, &fill);
6516
Bram Moolenaar6982f422019-02-16 16:48:01 +01006517 // Here we have to deal with a win32 console flake; See insert_lines()
6518 // above.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519
6520 nb = dest.Y + (source.Bottom - source.Top) + 1;
6521
6522 if (nb < source.Top)
6523 {
6524 COORD coord;
Bram Moolenaar6982f422019-02-16 16:48:01 +01006525 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526
Bram Moolenaar6982f422019-02-16 16:48:01 +01006527 coord.X = source.Left;
6528 for (i = nb; i < clip.Bottom; ++i)
6529 {
6530 coord.Y = i;
6531 clear_chars(coord, source.Right - source.Left + 1);
6532 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006533 }
Bram Moolenaar06b7b582020-05-30 17:49:25 +02006534
Christopher Plewright38804d62022-11-09 23:55:52 +00006535 if (vtp_working)
Bram Moolenaar06b7b582020-05-30 17:49:25 +02006536 {
6537 COORD coord;
6538 int i;
6539
6540 coord.X = source.Left;
6541 for (i = nb; i <= source.Bottom; ++i)
6542 {
6543 coord.Y = i;
6544 clear_chars(coord, source.Right - source.Left + 1);
6545 }
6546 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006547}
6548
6549
6550/*
Bram Moolenaar2313b612019-09-27 14:14:32 +02006551 * Set the cursor position to (x,y) (1-based).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552 */
6553 static void
6554gotoxy(
6555 unsigned x,
6556 unsigned y)
6557{
6558 if (x < 1 || x > (unsigned)Columns || y < 1 || y > (unsigned)Rows)
6559 return;
6560
Christopher Plewright38804d62022-11-09 23:55:52 +00006561 if (!(vtp_working
K.Takata161b6ac2022-11-14 15:31:07 +00006562# ifdef FEAT_TERMGUICOLORS
Christopher Plewright38804d62022-11-09 23:55:52 +00006563 && (p_tgc || t_colors >= 256)
K.Takata161b6ac2022-11-14 15:31:07 +00006564# endif
Christopher Plewright38804d62022-11-09 23:55:52 +00006565 ))
Bram Moolenaar2313b612019-09-27 14:14:32 +02006566 {
Bram Moolenaar82e743c2020-03-26 15:39:53 +01006567 // There are reports of double-width characters not displayed
6568 // correctly. This workaround should fix it, similar to how it's done
6569 // for VTP.
6570 g_coord.X = 0;
6571 SetConsoleCursorPosition(g_hConOut, g_coord);
6572
Bram Moolenaar2313b612019-09-27 14:14:32 +02006573 // external cursor coords are 1-based; internal are 0-based
6574 g_coord.X = x - 1;
6575 g_coord.Y = y - 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006576 SetConsoleCursorPosition(g_hConOut, g_coord);
Bram Moolenaar2313b612019-09-27 14:14:32 +02006577 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006578 else
Bram Moolenaar2313b612019-09-27 14:14:32 +02006579 {
6580 // Move the cursor to the left edge of the screen to prevent screen
Bram Moolenaara1299742019-10-10 16:36:00 +02006581 // destruction. Insider build bug. Always enabled because it's cheap
6582 // and avoids mistakes with recognizing the build.
6583 vtp_printf("\033[%d;%dH", g_coord.Y + 1, 1);
Bram Moolenaar2313b612019-09-27 14:14:32 +02006584
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006585 vtp_printf("\033[%d;%dH", y, x);
Bram Moolenaar2313b612019-09-27 14:14:32 +02006586
6587 g_coord.X = x - 1;
6588 g_coord.Y = y - 1;
6589 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590}
6591
6592
6593/*
6594 * Set the current text attribute = (foreground | background)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006595 * See ../runtime/doc/os_win32.txt for the numbers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006596 */
6597 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006598textattr(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006599{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006600 g_attrCurrent = wAttr & 0xff;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006601
6602 SetConsoleTextAttribute(g_hConOut, wAttr);
6603}
6604
6605
6606 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006607textcolor(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006609 g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006610
Christopher Plewright38804d62022-11-09 23:55:52 +00006611 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006612 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
6613 else
6614 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006615}
6616
6617
6618 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006619textbackground(WORD wAttr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006620{
Bram Moolenaar6383b922015-03-24 17:12:19 +01006621 g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006622
Christopher Plewright38804d62022-11-09 23:55:52 +00006623 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006624 SetConsoleTextAttribute(g_hConOut, g_attrCurrent);
6625 else
6626 vtp_sgr_bulk(wAttr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006627}
6628
6629
6630/*
6631 * restore the default text attribute (whatever we started with)
6632 */
6633 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006634normvideo(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006635{
Christopher Plewright38804d62022-11-09 23:55:52 +00006636 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006637 textattr(g_attrDefault);
6638 else
6639 vtp_sgr_bulk(0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006640}
6641
6642
6643static WORD g_attrPreStandout = 0;
6644
6645/*
6646 * Make the text standout, by brightening it
6647 */
6648 static void
6649standout(void)
6650{
6651 g_attrPreStandout = g_attrCurrent;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006652
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653 textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
6654}
6655
6656
6657/*
6658 * Turn off standout mode
6659 */
6660 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006661standend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006662{
6663 if (g_attrPreStandout)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006664 textattr(g_attrPreStandout);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006665
6666 g_attrPreStandout = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006667}
6668
6669
6670/*
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00006671 * Set normal fg/bg color, based on T_ME. Called when t_me has been set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006672 */
6673 void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006674mch_set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006675{
6676 char_u *p;
6677 int n;
6678
6679 cterm_normal_fg_color = (g_attrDefault & 0xf) + 1;
6680 cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006681 if (
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006682# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006683 !p_tgc &&
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006684# endif
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006685 T_ME[0] == ESC && T_ME[1] == '|')
Bram Moolenaar071d4272004-06-13 20:20:40 +00006686 {
6687 p = T_ME + 2;
6688 n = getdigits(&p);
6689 if (*p == 'm' && n > 0)
6690 {
6691 cterm_normal_fg_color = (n & 0xf) + 1;
6692 cterm_normal_bg_color = ((n >> 4) & 0xf) + 1;
6693 }
6694 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006695# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006696 cterm_normal_fg_gui_color = INVALCOLOR;
6697 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006698# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006699}
6700
6701
6702/*
6703 * visual bell: flash the screen
6704 */
6705 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006706visual_bell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006707{
6708 COORD coordOrigin = {0, 0};
6709 WORD attrFlash = ~g_attrCurrent & 0xff;
6710
6711 DWORD dwDummy;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02006712 LPWORD oldattrs = ALLOC_MULT(WORD, Rows * Columns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006713
6714 if (oldattrs == NULL)
6715 return;
6716 ReadConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
6717 coordOrigin, &dwDummy);
6718 FillConsoleOutputAttribute(g_hConOut, attrFlash, Rows * Columns,
6719 coordOrigin, &dwDummy);
6720
Bram Moolenaar0f873732019-12-05 20:28:46 +01006721 Sleep(15); // wait for 15 msec
Christopher Plewright38804d62022-11-09 23:55:52 +00006722 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01006723 WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns,
Bram Moolenaar071d4272004-06-13 20:20:40 +00006724 coordOrigin, &dwDummy);
6725 vim_free(oldattrs);
6726}
6727
6728
6729/*
6730 * Make the cursor visible or invisible
6731 */
6732 static void
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00006733cursor_visible(BOOL fVisible)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006734{
6735 s_cursor_visible = fVisible;
Bram Moolenaar2695de62020-04-17 21:13:01 +02006736
Christopher Plewright38804d62022-11-09 23:55:52 +00006737 if (vtp_working)
Bram Moolenaar2695de62020-04-17 21:13:01 +02006738 vtp_printf("\033[?25%c", fVisible ? 'h' : 'l');
6739
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006740# ifdef MCH_CURSOR_SHAPE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006741 mch_update_cursor();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01006742# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006743}
6744
6745
6746/*
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02006747 * Write "cbToWrite" bytes in `pchBuf' to the screen.
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006748 * Returns the number of bytes actually written (at least one).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006749 */
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006750 static DWORD
Bram Moolenaar071d4272004-06-13 20:20:40 +00006751write_chars(
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006752 char_u *pchBuf,
6753 DWORD cbToWrite)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006754{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006755 COORD coord = g_coord;
6756 DWORD written;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006757 DWORD n, cchwritten;
6758 static DWORD cells;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006759 static WCHAR *unicodebuf = NULL;
6760 static int unibuflen = 0;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006761 static int length;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006762 int cp = enc_utf8 ? CP_UTF8 : enc_codepage;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006763 static WCHAR *utf8spbuf = NULL;
6764 static int utf8splength;
6765 static DWORD utf8spcells;
6766 static WCHAR **utf8usingbuf = &unicodebuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006767
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006768 if (cbToWrite != 1 || *pchBuf != ' ' || !enc_utf8)
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006769 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006770 utf8usingbuf = &unicodebuf;
6771 do
6772 {
6773 length = MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite,
6774 unicodebuf, unibuflen);
6775 if (length && length <= unibuflen)
6776 break;
6777 vim_free(unicodebuf);
6778 unicodebuf = length ? LALLOC_MULT(WCHAR, length) : NULL;
6779 unibuflen = unibuflen ? 0 : length;
6780 } while(1);
6781 cells = mb_string2cells(pchBuf, cbToWrite);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006782 }
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006783 else // cbToWrite == 1 && *pchBuf == ' ' && enc_utf8
6784 {
6785 if (utf8usingbuf != &utf8spbuf)
6786 {
6787 if (utf8spbuf == NULL)
6788 {
6789 cells = mb_string2cells((char_u *)" ", 1);
6790 length = MultiByteToWideChar(CP_UTF8, 0, " ", 1, NULL, 0);
6791 utf8spbuf = LALLOC_MULT(WCHAR, length);
6792 if (utf8spbuf != NULL)
6793 {
6794 MultiByteToWideChar(CP_UTF8, 0, " ", 1, utf8spbuf, length);
6795 utf8usingbuf = &utf8spbuf;
6796 utf8splength = length;
6797 utf8spcells = cells;
6798 }
6799 }
6800 else
6801 {
6802 utf8usingbuf = &utf8spbuf;
6803 length = utf8splength;
6804 cells = utf8spcells;
6805 }
6806 }
6807 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006808
Christopher Plewright38804d62022-11-09 23:55:52 +00006809 if (!(vtp_working
K.Takata161b6ac2022-11-14 15:31:07 +00006810# ifdef FEAT_TERMGUICOLORS
Christopher Plewright38804d62022-11-09 23:55:52 +00006811 && (p_tgc || t_colors >= 256)
K.Takata161b6ac2022-11-14 15:31:07 +00006812# endif
Christopher Plewright38804d62022-11-09 23:55:52 +00006813 ))
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006814 {
6815 FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells,
6816 coord, &written);
6817 // When writing fails or didn't write a single character, pretend one
6818 // character was written, otherwise we get stuck.
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006819 if (WriteConsoleOutputCharacterW(g_hConOut, *utf8usingbuf, length,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006820 coord, &cchwritten) == 0
6821 || cchwritten == 0 || cchwritten == (DWORD)-1)
6822 cchwritten = 1;
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006823 }
6824 else
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006825 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006826 if (WriteConsoleW(g_hConOut, *utf8usingbuf, length, &cchwritten,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006827 NULL) == 0 || cchwritten == 0)
6828 cchwritten = 1;
6829 }
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006830
K.Takata135e1522022-01-29 15:27:58 +00006831 if (cchwritten == (DWORD)length)
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02006832 {
6833 written = cbToWrite;
6834 g_coord.X += (SHORT)cells;
6835 }
6836 else
6837 {
6838 char_u *p = pchBuf;
6839 for (n = 0; n < cchwritten; n++)
6840 MB_CPTR_ADV(p);
6841 written = p - pchBuf;
6842 g_coord.X += (SHORT)mb_string2cells(pchBuf, written);
Bram Moolenaarac360bf2015-09-01 20:31:20 +02006843 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006844
6845 while (g_coord.X > g_srScrollRegion.Right)
6846 {
6847 g_coord.X -= (SHORT) Columns;
6848 if (g_coord.Y < g_srScrollRegion.Bottom)
6849 g_coord.Y++;
6850 }
6851
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006852 // Cursor under VTP is always in the correct position, no need to reset.
Christopher Plewright38804d62022-11-09 23:55:52 +00006853 if (!(vtp_working
K.Takata161b6ac2022-11-14 15:31:07 +00006854# ifdef FEAT_TERMGUICOLORS
Christopher Plewright38804d62022-11-09 23:55:52 +00006855 && (p_tgc || t_colors >= 256)
K.Takata161b6ac2022-11-14 15:31:07 +00006856# endif
Christopher Plewright38804d62022-11-09 23:55:52 +00006857 ))
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006858 gotoxy(g_coord.X + 1, g_coord.Y + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006859
6860 return written;
6861}
6862
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006863 static char_u *
6864get_seq(
6865 int *args,
6866 int *count,
6867 char_u *head)
6868{
6869 int argc;
6870 char_u *p;
6871
6872 if (head == NULL || *head != '\033')
6873 return NULL;
6874
6875 argc = 0;
6876 p = head;
6877 ++p;
6878 do
6879 {
6880 ++p;
6881 args[argc] = getdigits(&p);
6882 argc += (argc < 15) ? 1 : 0;
6883 } while (*p == ';');
6884 *count = argc;
6885
6886 return p;
6887}
6888
6889 static char_u *
6890get_sgr(
6891 int *args,
6892 int *count,
6893 char_u *head)
6894{
6895 char_u *p = get_seq(args, count, head);
6896
6897 return (p && *p == 'm') ? ++p : NULL;
6898}
6899
6900/*
6901 * Pointer to next if SGR (^[[n;2;*;*;*m), NULL otherwise.
6902 */
6903 static char_u *
6904sgrn2(
6905 char_u *head,
6906 int n)
6907{
6908 int argc;
6909 int args[16];
6910 char_u *p = get_sgr(args, &argc, head);
6911
6912 return p && argc == 5 && args[0] == n && args[1] == 2 ? p : NULL;
6913}
6914
6915/*
6916 * Pointer to next if SGR(^[[nm)<space>ESC, NULL otherwise.
6917 */
6918 static char_u *
6919sgrnc(
6920 char_u *head,
6921 int n)
6922{
6923 int argc;
6924 int args[16];
6925 char_u *p = get_sgr(args, &argc, head);
6926
6927 return p && argc == 1 && args[0] == n && (p = skipwhite(p)) && *p == '\033'
6928 ? p : NULL;
6929}
6930
6931 static char_u *
6932skipblank(char_u *q)
6933{
6934 char_u *p = q;
6935
6936 while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
6937 ++p;
6938 return p;
6939}
6940
6941/*
6942 * Pointer to the next if any whitespace that may follow SGR is ESC, otherwise
6943 * NULL.
6944 */
6945 static char_u *
6946sgrn2c(
6947 char_u *head,
6948 int n)
6949{
6950 char_u *p = sgrn2(head, n);
6951
6952 return p && *p != NUL && (p = skipblank(p)) && *p == '\033' ? p : NULL;
6953}
6954
6955/*
6956 * If there is only a newline between the sequence immediately following it,
6957 * a pointer to the character following the newline is returned.
6958 * Otherwise NULL.
6959 */
6960 static char_u *
6961sgrn2cn(
6962 char_u *head,
6963 int n)
6964{
6965 char_u *p = sgrn2(head, n);
6966
6967 return p && p[0] == 0x0a && p[1] == '\033' ? ++p : NULL;
6968}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006969
6970/*
6971 * mch_write(): write the output buffer to the screen, translating ESC
6972 * sequences into calls to console output routines.
6973 */
6974 void
6975mch_write(
6976 char_u *s,
6977 int len)
6978{
Bram Moolenaard23f8bd2021-04-21 11:30:48 +02006979 char_u *end = s + len;
6980
Bram Moolenaarafde13b2019-04-28 19:46:49 +02006981# ifdef VIMDLL
6982 if (gui.in_use)
6983 return;
6984# endif
6985
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986 if (!term_console)
6987 {
6988 write(1, s, (unsigned)len);
6989 return;
6990 }
6991
Bram Moolenaar0f873732019-12-05 20:28:46 +01006992 // translate ESC | sequences into faked bios calls
Bram Moolenaar071d4272004-06-13 20:20:40 +00006993 while (len--)
6994 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02006995 int prefix = -1;
6996 char_u ch;
6997
6998 // While processing a sequence, on rare occasions it seems that another
6999 // sequence may be inserted asynchronously.
7000 if (len < 0)
7001 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +01007002 redraw_all_later(UPD_CLEAR);
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007003 return;
7004 }
7005
Bram Moolenaard23f8bd2021-04-21 11:30:48 +02007006 while (s + ++prefix < end)
7007 {
7008 ch = s[prefix];
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007009 if (ch <= 0x1e && !(ch != '\n' && ch != '\r' && ch != '\b'
7010 && ch != '\a' && ch != '\033'))
7011 break;
Bram Moolenaard23f8bd2021-04-21 11:30:48 +02007012 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007013
7014 if (p_wd)
7015 {
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02007016 WaitForChar(p_wd, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007017 if (prefix != 0)
7018 prefix = 1;
7019 }
7020
7021 if (prefix != 0)
7022 {
7023 DWORD nWritten;
7024
7025 nWritten = write_chars(s, prefix);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007026# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007027 if (fdDump)
7028 {
7029 fputc('>', fdDump);
7030 fwrite(s, sizeof(char_u), nWritten, fdDump);
7031 fputs("<\n", fdDump);
7032 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007033# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007034 len -= (nWritten - 1);
7035 s += nWritten;
7036 }
7037 else if (s[0] == '\n')
7038 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007039 // \n, newline: go to the beginning of the next line or scroll
Bram Moolenaar071d4272004-06-13 20:20:40 +00007040 if (g_coord.Y == g_srScrollRegion.Bottom)
7041 {
7042 scroll(1);
7043 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
7044 }
7045 else
7046 {
7047 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
7048 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007049# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007050 if (fdDump)
7051 fputs("\\n\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007052# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007053 s++;
7054 }
7055 else if (s[0] == '\r')
7056 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007057 // \r, carriage return: go to beginning of line
Bram Moolenaar071d4272004-06-13 20:20:40 +00007058 gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007059# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007060 if (fdDump)
7061 fputs("\\r\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007062# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007063 s++;
7064 }
7065 else if (s[0] == '\b')
7066 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007067 // \b, backspace: move cursor one position left
Bram Moolenaar071d4272004-06-13 20:20:40 +00007068 if (g_coord.X > g_srScrollRegion.Left)
7069 g_coord.X--;
7070 else if (g_coord.Y > g_srScrollRegion.Top)
7071 {
7072 g_coord.X = g_srScrollRegion.Right;
7073 g_coord.Y--;
7074 }
7075 gotoxy(g_coord.X + 1, g_coord.Y + 1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007076# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007077 if (fdDump)
7078 fputs("\\b\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007079# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007080 s++;
7081 }
7082 else if (s[0] == '\a')
7083 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007084 // \a, bell
Bram Moolenaar071d4272004-06-13 20:20:40 +00007085 MessageBeep(0xFFFFFFFF);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007086# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007087 if (fdDump)
7088 fputs("\\a\n", fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007089# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007090 s++;
7091 }
7092 else if (s[0] == ESC && len >= 3-1 && s[1] == '|')
7093 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007094# ifdef MCH_WRITE_DUMP
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007095 char_u *old_s = s;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007096# endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007097 char_u *p;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007098 int arg1 = 0, arg2 = 0, argc = 0, args[16];
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007099 char_u *sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007100
7101 switch (s[2])
7102 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007103 case '0': case '1': case '2': case '3': case '4':
7104 case '5': case '6': case '7': case '8': case '9':
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007105 if (*(p = get_seq(args, &argc, s)) != 'm')
7106 goto notsgr;
7107
7108 p = s;
7109
7110 // Handling frequent optional sequences. Output to the screen
7111 // takes too long, so do not output as much as possible.
7112
7113 // If resetFG,FG,BG,<cr>,BG,FG are connected, the preceding
7114 // resetFG,FG,BG are omitted.
7115 if (sgrn2(sgrn2(sgrn2cn(sgrn2(sgrnc(p, 39), 38), 48), 48), 38))
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007116 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007117 p = sgrn2(sgrn2(sgrnc(p, 39), 38), 48);
7118 len = len + 1 - (int)(p - s);
7119 s = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007120 break;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007121 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007122
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007123 // If FG,BG,BG,FG of SGR are connected, the first FG can be
7124 // omitted.
7125 if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 48), 38))
7126 p = sp;
7127
7128 // If FG,BG,FG,BG of SGR are connected, the first FG can be
7129 // omitted.
7130 if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 38), 48))
7131 p = sp;
7132
7133 // If BG,BG of SGR are connected, the first BG can be omitted.
7134 if (sgrn2((sp = sgrn2(p, 48)), 48))
7135 p = sp;
7136
7137 // If restoreFG and FG are connected, the restoreFG can be
Bram Moolenaar9f1983d2022-05-12 20:35:35 +01007138 // omitted.
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02007139 if (sgrn2((sp = sgrnc(p, 39)), 38))
7140 p = sp;
7141
7142 p = get_seq(args, &argc, p);
7143
7144notsgr:
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007145 arg1 = args[0];
7146 arg2 = args[1];
7147 if (*p == 'm')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007148 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007149 if (argc == 1 && args[0] == 0)
7150 normvideo();
7151 else if (argc == 1)
7152 {
Christopher Plewright38804d62022-11-09 23:55:52 +00007153 if (vtp_working
7154# ifdef FEAT_TERMGUICOLORS
7155 && (p_tgc || t_colors >= 256)
7156# endif
7157 )
7158 textcolor((WORD)arg1);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007159 else
Christopher Plewright38804d62022-11-09 23:55:52 +00007160 textattr((WORD)arg1);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007161 }
Christopher Plewright38804d62022-11-09 23:55:52 +00007162 else if (vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007163 vtp_sgr_bulks(argc, args);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007164 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007165 else if (argc == 2 && *p == 'H')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007166 {
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007167 gotoxy(arg2, arg1);
7168 }
7169 else if (argc == 2 && *p == 'r')
7170 {
7171 set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
7172 }
Bram Moolenaar6982f422019-02-16 16:48:01 +01007173 else if (argc == 2 && *p == 'R')
7174 {
7175 set_scroll_region_tb(arg1, arg2);
7176 }
7177 else if (argc == 2 && *p == 'V')
7178 {
7179 set_scroll_region_lr(arg1, arg2);
7180 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007181 else if (argc == 1 && *p == 'A')
7182 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007183 gotoxy(g_coord.X + 1,
7184 max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
7185 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007186 else if (argc == 1 && *p == 'b')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007187 {
7188 textbackground((WORD) arg1);
7189 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01007190 else if (argc == 1 && *p == 'C')
7191 {
7192 gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
7193 g_coord.Y + 1);
7194 }
7195 else if (argc == 1 && *p == 'f')
7196 {
7197 textcolor((WORD) arg1);
7198 }
7199 else if (argc == 1 && *p == 'H')
7200 {
7201 gotoxy(1, arg1);
7202 }
7203 else if (argc == 1 && *p == 'L')
7204 {
7205 insert_lines(arg1);
7206 }
7207 else if (argc == 1 && *p == 'M')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007208 {
7209 delete_lines(arg1);
7210 }
7211
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007212 len -= (int)(p - s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007213 s = p + 1;
7214 break;
7215
Bram Moolenaar071d4272004-06-13 20:20:40 +00007216 case 'A':
Bram Moolenaar071d4272004-06-13 20:20:40 +00007217 gotoxy(g_coord.X + 1,
7218 max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
7219 goto got3;
7220
7221 case 'B':
7222 visual_bell();
7223 goto got3;
7224
7225 case 'C':
Bram Moolenaar071d4272004-06-13 20:20:40 +00007226 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
7227 g_coord.Y + 1);
7228 goto got3;
7229
7230 case 'E':
7231 termcap_mode_end();
7232 goto got3;
7233
7234 case 'F':
7235 standout();
7236 goto got3;
7237
7238 case 'f':
7239 standend();
7240 goto got3;
7241
7242 case 'H':
7243 gotoxy(1, 1);
7244 goto got3;
7245
7246 case 'j':
7247 clear_to_end_of_display();
7248 goto got3;
7249
7250 case 'J':
7251 clear_screen();
7252 goto got3;
7253
7254 case 'K':
7255 clear_to_end_of_line();
7256 goto got3;
7257
7258 case 'L':
7259 insert_lines(1);
7260 goto got3;
7261
7262 case 'M':
7263 delete_lines(1);
7264 goto got3;
7265
7266 case 'S':
7267 termcap_mode_start();
7268 goto got3;
7269
7270 case 'V':
7271 cursor_visible(TRUE);
7272 goto got3;
7273
7274 case 'v':
7275 cursor_visible(FALSE);
7276 goto got3;
7277
7278 got3:
7279 s += 3;
7280 len -= 2;
7281 }
7282
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007283# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007284 if (fdDump)
7285 {
7286 fputs("ESC | ", fdDump);
7287 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
7288 fputc('\n', fdDump);
7289 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007290# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007291 }
K.Takatadf5320c2022-09-01 13:20:16 +01007292 else if (s[0] == ESC && len >= 3-1 && s[1] == '[')
7293 {
7294 int l = 2;
7295
7296 if (isdigit(s[l]))
7297 l++;
7298 if (s[l] == ' ' && s[l + 1] == 'q')
7299 {
7300 // DECSCUSR (cursor style) sequences
Christopher Plewright38804d62022-11-09 23:55:52 +00007301 if (vtp_working)
K.Takatadf5320c2022-09-01 13:20:16 +01007302 vtp_printf("%.*s", l + 2, s); // Pass through
7303 s += l + 2;
7304 len -= l + 1;
7305 }
7306 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007307 else
7308 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007309 // Write a single character
Bram Moolenaar071d4272004-06-13 20:20:40 +00007310 DWORD nWritten;
7311
7312 nWritten = write_chars(s, 1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007313# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007314 if (fdDump)
7315 {
7316 fputc('>', fdDump);
7317 fwrite(s, sizeof(char_u), nWritten, fdDump);
7318 fputs("<\n", fdDump);
7319 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007320# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007321
7322 len -= (nWritten - 1);
7323 s += nWritten;
7324 }
7325 }
7326
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007327# ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00007328 if (fdDump)
7329 fflush(fdDump);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01007330# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007331}
7332
Bram Moolenaar0f873732019-12-05 20:28:46 +01007333#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00007334
7335
7336/*
Bram Moolenaar0e0b3dd2016-03-17 17:58:56 +01007337 * Delay for "msec" milliseconds.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007338 */
7339 void
7340mch_delay(
7341 long msec,
Bram Moolenaar0981c872020-08-23 14:28:37 +02007342 int flags UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007343{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007344#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01007345 Sleep((int)msec); // never wait for input
7346#else // Console
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007347# ifdef VIMDLL
7348 if (gui.in_use)
7349 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007350 Sleep((int)msec); // never wait for input
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007351 return;
7352 }
7353# endif
Bram Moolenaar0981c872020-08-23 14:28:37 +02007354 if (flags & MCH_DELAY_IGNOREINPUT)
Bram Moolenaar325b7a22004-07-05 15:58:32 +00007355# ifdef FEAT_MZSCHEME
7356 if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
7357 {
7358 int towait = p_mzq;
7359
Bram Moolenaar0f873732019-12-05 20:28:46 +01007360 // if msec is large enough, wait by portions in p_mzq
Bram Moolenaar325b7a22004-07-05 15:58:32 +00007361 while (msec > 0)
7362 {
7363 mzvim_check_threads();
7364 if (msec < towait)
7365 towait = msec;
7366 Sleep(towait);
7367 msec -= towait;
7368 }
7369 }
7370 else
7371# endif
7372 Sleep((int)msec);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007373 else
Bram Moolenaare9c21ae2017-08-03 20:44:48 +02007374 WaitForChar(msec, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007375#endif
7376}
7377
7378
7379/*
Bram Moolenaar203258c2016-01-17 22:15:16 +01007380 * This version of remove is not scared by a readonly (backup) file.
7381 * This can also remove a symbolic link like Unix.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007382 * Return 0 for success, -1 for failure.
7383 */
7384 int
7385mch_remove(char_u *name)
7386{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007387 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007388 int n;
7389
Bram Moolenaar203258c2016-01-17 22:15:16 +01007390 /*
7391 * On Windows, deleting a directory's symbolic link is done by
7392 * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact.
7393 */
7394 if (mch_isdir(name) && mch_is_symbolic_link(name))
7395 return mch_rmdir(name);
7396
Bram Moolenaar12b559e2013-06-12 22:41:37 +02007397 win32_setattrs(name, FILE_ATTRIBUTE_NORMAL);
7398
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007399 wn = enc_to_utf16(name, NULL);
7400 if (wn == NULL)
7401 return -1;
7402
7403 n = DeleteFileW(wn) ? 0 : -1;
7404 vim_free(wn);
7405 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007406}
7407
7408
7409/*
Bram Moolenaarb9c31e72016-09-29 15:18:57 +02007410 * Check for an "interrupt signal": CTRL-break or CTRL-C.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007411 */
7412 void
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02007413mch_breakcheck(int force UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007414{
Bram Moolenaarafde13b2019-04-28 19:46:49 +02007415#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
7416# ifdef VIMDLL
7417 if (!gui.in_use)
7418# endif
7419 if (g_fCtrlCPressed || g_fCBrkPressed)
7420 {
7421 ctrl_break_was_pressed = g_fCBrkPressed;
7422 g_fCtrlCPressed = g_fCBrkPressed = FALSE;
7423 got_int = TRUE;
7424 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007425#endif
7426}
7427
Bram Moolenaar0f873732019-12-05 20:28:46 +01007428// physical RAM to leave for the OS
Bram Moolenaaree273972016-01-02 21:11:51 +01007429#define WINNT_RESERVE_BYTES (256*1024*1024)
Bram Moolenaaree273972016-01-02 21:11:51 +01007430
7431/*
7432 * How much main memory in KiB that can be used by VIM.
7433 */
Bram Moolenaaree273972016-01-02 21:11:51 +01007434 long_u
Bram Moolenaar1266d672017-02-01 13:43:36 +01007435mch_total_mem(int special UNUSED)
Bram Moolenaaree273972016-01-02 21:11:51 +01007436{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007437 MEMORYSTATUSEX ms;
7438
Bram Moolenaar0f873732019-12-05 20:28:46 +01007439 // Need to use GlobalMemoryStatusEx() when there is more memory than
7440 // what fits in 32 bits.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007441 ms.dwLength = sizeof(MEMORYSTATUSEX);
7442 GlobalMemoryStatusEx(&ms);
7443 if (ms.ullAvailVirtual < ms.ullTotalPhys)
Bram Moolenaaree273972016-01-02 21:11:51 +01007444 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007445 // Process address space fits in physical RAM, use all of it.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007446 return (long_u)(ms.ullAvailVirtual / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01007447 }
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007448 if (ms.ullTotalPhys <= WINNT_RESERVE_BYTES)
Bram Moolenaaree273972016-01-02 21:11:51 +01007449 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007450 // Catch old NT box or perverse hardware setup.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007451 return (long_u)((ms.ullTotalPhys / 2) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01007452 }
Bram Moolenaar0f873732019-12-05 20:28:46 +01007453 // Use physical RAM less reserve for OS + data.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007454 return (long_u)((ms.ullTotalPhys - WINNT_RESERVE_BYTES) / 1024);
Bram Moolenaaree273972016-01-02 21:11:51 +01007455}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007456
Bram Moolenaar071d4272004-06-13 20:20:40 +00007457/*
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007458 * mch_wrename() works around a bug in rename (aka MoveFile) in
Bram Moolenaar071d4272004-06-13 20:20:40 +00007459 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
7460 * file whose short file name is "FOO.BAR" (its long file name will
7461 * be correct: "foo.bar~"). Because a file can be accessed by
7462 * either its SFN or its LFN, "foo.bar" has effectively been
7463 * renamed to "foo.bar", which is not at all what was wanted. This
7464 * seems to happen only when renaming files with three-character
7465 * extensions by appending a suffix that does not include ".".
7466 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
7467 *
7468 * There is another problem, which isn't really a bug but isn't right either:
7469 * When renaming "abcdef~1.txt" to "abcdef~1.txt~", the short name can be
7470 * "abcdef~1.txt" again. This has been reported on Windows NT 4.0 with
7471 * service pack 6. Doesn't seem to happen on Windows 98.
7472 *
7473 * Like rename(), returns 0 upon success, non-zero upon failure.
7474 * Should probably set errno appropriately when errors occur.
7475 */
7476 int
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007477mch_wrename(WCHAR *wold, WCHAR *wnew)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007478{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007479 WCHAR *p;
7480 int i;
7481 WCHAR szTempFile[_MAX_PATH + 1];
7482 WCHAR szNewPath[_MAX_PATH + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00007483 HANDLE hf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007484
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007485 // No need to play tricks unless the file name contains a "~" as the
7486 // seventh character.
7487 p = wold;
7488 for (i = 0; wold[i] != NUL; ++i)
7489 if ((wold[i] == '/' || wold[i] == '\\' || wold[i] == ':')
7490 && wold[i + 1] != 0)
7491 p = wold + i + 1;
7492 if ((int)(wold + i - p) < 8 || p[6] != '~')
7493 return (MoveFileW(wold, wnew) == 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007494
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007495 // Get base path of new file name. Undocumented feature: If pszNewFile is
7496 // a directory, no error is returned and pszFilePart will be NULL.
7497 if (GetFullPathNameW(wnew, _MAX_PATH, szNewPath, &p) == 0 || p == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007498 return -1;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007499 *p = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007500
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007501 // Get (and create) a unique temporary file name in directory of new file
7502 if (GetTempFileNameW(szNewPath, L"VIM", 0, szTempFile) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007503 return -2;
7504
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007505 // blow the temp file away
7506 if (!DeleteFileW(szTempFile))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007507 return -3;
7508
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007509 // rename old file to the temp file
7510 if (!MoveFileW(wold, szTempFile))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007511 return -4;
7512
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007513 // now create an empty file called pszOldFile; this prevents the operating
7514 // system using pszOldFile as an alias (SFN) if we're renaming within the
7515 // same directory. For example, we're editing a file called
7516 // filename.asc.txt by its SFN, filena~1.txt. If we rename filena~1.txt
7517 // to filena~1.txt~ (i.e., we're making a backup while writing it), the
7518 // SFN for filena~1.txt~ will be filena~1.txt, by default, which will
7519 // cause all sorts of problems later in buf_write(). So, we create an
7520 // empty file called filena~1.txt and the system will have to find some
7521 // other SFN for filena~1.txt~, such as filena~2.txt
7522 if ((hf = CreateFileW(wold, GENERIC_WRITE, 0, NULL, CREATE_NEW,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007523 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
7524 return -5;
7525 if (!CloseHandle(hf))
7526 return -6;
7527
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007528 // rename the temp file to the new file
7529 if (!MoveFileW(szTempFile, wnew))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007530 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007531 // Renaming failed. Rename the file back to its old name, so that it
7532 // looks like nothing happened.
7533 (void)MoveFileW(szTempFile, wold);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007534 return -7;
7535 }
7536
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007537 // Seems to be left around on Novell filesystems
7538 DeleteFileW(szTempFile);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007539
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007540 // finally, remove the empty old file
7541 if (!DeleteFileW(wold))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007542 return -8;
7543
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007544 return 0;
7545}
7546
7547
7548/*
7549 * Converts the filenames to UTF-16, then call mch_wrename().
7550 * Like rename(), returns 0 upon success, non-zero upon failure.
7551 */
7552 int
7553mch_rename(
7554 const char *pszOldFile,
7555 const char *pszNewFile)
7556{
7557 WCHAR *wold = NULL;
7558 WCHAR *wnew = NULL;
7559 int retval = -1;
7560
7561 wold = enc_to_utf16((char_u *)pszOldFile, NULL);
7562 wnew = enc_to_utf16((char_u *)pszNewFile, NULL);
7563 if (wold != NULL && wnew != NULL)
7564 retval = mch_wrename(wold, wnew);
7565 vim_free(wold);
7566 vim_free(wnew);
7567 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007568}
7569
7570/*
7571 * Get the default shell for the current hardware platform
7572 */
7573 char *
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007574default_shell(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007575{
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007576 return "cmd.exe";
Bram Moolenaar071d4272004-06-13 20:20:40 +00007577}
7578
7579/*
7580 * mch_access() extends access() to do more detailed check on network drives.
7581 * Returns 0 if file "n" has access rights according to "p", -1 otherwise.
7582 */
7583 int
7584mch_access(char *n, int p)
7585{
7586 HANDLE hFile;
Bram Moolenaar0f873732019-12-05 20:28:46 +01007587 int retval = -1; // default: fail
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007588 WCHAR *wn;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007589
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007590 wn = enc_to_utf16((char_u *)n, NULL);
7591 if (wn == NULL)
7592 return -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007593
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01007594 if (mch_isdir((char_u *)n))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007595 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007596 WCHAR TempNameW[_MAX_PATH + 16] = L"";
Bram Moolenaar071d4272004-06-13 20:20:40 +00007597
7598 if (p & R_OK)
7599 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007600 // Read check is performed by seeing if we can do a find file on
7601 // the directory for any file.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007602 int i;
7603 WIN32_FIND_DATAW d;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007604
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007605 for (i = 0; i < _MAX_PATH && wn[i] != 0; ++i)
7606 TempNameW[i] = wn[i];
7607 if (TempNameW[i - 1] != '\\' && TempNameW[i - 1] != '/')
7608 TempNameW[i++] = '\\';
7609 TempNameW[i++] = '*';
7610 TempNameW[i++] = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007611
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007612 hFile = FindFirstFileW(TempNameW, &d);
7613 if (hFile == INVALID_HANDLE_VALUE)
7614 goto getout;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007615 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007616 (void)FindClose(hFile);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007617 }
7618
7619 if (p & W_OK)
7620 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007621 // Trying to create a temporary file in the directory should catch
7622 // directories on read-only network shares. However, in
7623 // directories whose ACL allows writes but denies deletes will end
7624 // up keeping the temporary file :-(.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007625 if (!GetTempFileNameW(wn, L"VIM", 0, TempNameW))
7626 goto getout;
Bram Moolenaarcea912a2016-10-12 14:20:24 +02007627 else
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007628 DeleteFileW(TempNameW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007629 }
7630 }
7631 else
7632 {
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007633 // Don't consider a file read-only if another process has opened it.
7634 DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
7635
Bram Moolenaar0f873732019-12-05 20:28:46 +01007636 // Trying to open the file for the required access does ACL, read-only
7637 // network share, and file attribute checks.
Bram Moolenaar5aa98962018-05-06 17:09:38 +02007638 DWORD access_mode = ((p & W_OK) ? GENERIC_WRITE : 0)
7639 | ((p & R_OK) ? GENERIC_READ : 0);
7640
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007641 hFile = CreateFileW(wn, access_mode, share_mode,
7642 NULL, OPEN_EXISTING, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007643 if (hFile == INVALID_HANDLE_VALUE)
7644 goto getout;
7645 CloseHandle(hFile);
7646 }
7647
Bram Moolenaar0f873732019-12-05 20:28:46 +01007648 retval = 0; // success
Bram Moolenaar071d4272004-06-13 20:20:40 +00007649getout:
Bram Moolenaar071d4272004-06-13 20:20:40 +00007650 vim_free(wn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007651 return retval;
7652}
7653
Bram Moolenaar071d4272004-06-13 20:20:40 +00007654/*
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007655 * Version of open() that may use UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007656 */
7657 int
Bram Moolenaarb6843a02017-08-02 22:07:12 +02007658mch_open(const char *name, int flags, int mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007659{
7660 WCHAR *wn;
7661 int f;
7662
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007663 wn = enc_to_utf16((char_u *)name, NULL);
7664 if (wn == NULL)
7665 return -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007666
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007667 f = _wopen(wn, flags, mode);
7668 vim_free(wn);
7669 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007670}
7671
7672/*
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007673 * Version of fopen() that uses UTF-16 file name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007674 */
7675 FILE *
Bram Moolenaarb6843a02017-08-02 22:07:12 +02007676mch_fopen(const char *name, const char *mode)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007677{
7678 WCHAR *wn, *wm;
7679 FILE *f = NULL;
7680
Bram Moolenaara12a1612019-01-24 16:39:02 +01007681#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0f873732019-12-05 20:28:46 +01007682 // Work around an annoying assertion in the Microsoft debug CRT
7683 // when mode's text/binary setting doesn't match _get_fmode().
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007684 char newMode = mode[strlen(mode) - 1];
7685 int oldMode = 0;
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007686
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007687 _get_fmode(&oldMode);
7688 if (newMode == 't')
7689 _set_fmode(_O_TEXT);
7690 else if (newMode == 'b')
7691 _set_fmode(_O_BINARY);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007692#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007693 wn = enc_to_utf16((char_u *)name, NULL);
7694 wm = enc_to_utf16((char_u *)mode, NULL);
7695 if (wn != NULL && wm != NULL)
7696 f = _wfopen(wn, wm);
7697 vim_free(wn);
7698 vim_free(wm);
Bram Moolenaar0fde2902008-03-16 13:54:13 +00007699
Bram Moolenaara12a1612019-01-24 16:39:02 +01007700#if defined(DEBUG) && _MSC_VER >= 1400
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007701 _set_fmode(oldMode);
Bram Moolenaara12a1612019-01-24 16:39:02 +01007702#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02007703 return f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007704}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007705
Bram Moolenaar071d4272004-06-13 20:20:40 +00007706/*
7707 * SUB STREAM (aka info stream) handling:
7708 *
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01007709 * NTFS can have sub streams for each file. The normal contents of a file is
7710 * stored in the main stream, and extra contents (author information, title and
7711 * so on) can be stored in a sub stream. After Windows 2000, the user can
7712 * access and store this information in sub streams via an explorer's property
7713 * menu item in the right click menu. This information in sub streams was lost
7714 * when copying only the main stream. Therefore we have to copy sub streams.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007715 *
7716 * Incomplete explanation:
7717 * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp
7718 * More useful info and an example:
7719 * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams
7720 */
7721
7722/*
7723 * Copy info stream data "substream". Read from the file with BackupRead(sh)
7724 * and write to stream "substream" of file "to".
7725 * Errors are ignored.
7726 */
7727 static void
7728copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len)
7729{
7730 HANDLE hTo;
7731 WCHAR *to_name;
7732
7733 to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR));
7734 wcscpy(to_name, to);
7735 wcscat(to_name, substream);
7736
7737 hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
7738 FILE_ATTRIBUTE_NORMAL, NULL);
7739 if (hTo != INVALID_HANDLE_VALUE)
7740 {
7741 long done;
7742 DWORD todo;
7743 DWORD readcnt, written;
7744 char buf[4096];
7745
Bram Moolenaar0f873732019-12-05 20:28:46 +01007746 // Copy block of bytes at a time. Abort when something goes wrong.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007747 for (done = 0; done < len; done += written)
7748 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007749 // (size_t) cast for Borland C 5.5
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007750 todo = (DWORD)((size_t)(len - done) > sizeof(buf) ? sizeof(buf)
7751 : (size_t)(len - done));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007752 if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt,
7753 FALSE, FALSE, context)
7754 || readcnt != todo
7755 || !WriteFile(hTo, buf, todo, &written, NULL)
7756 || written != todo)
7757 break;
7758 }
7759 CloseHandle(hTo);
7760 }
7761
7762 free(to_name);
7763}
7764
7765/*
7766 * Copy info streams from file "from" to file "to".
7767 */
7768 static void
7769copy_infostreams(char_u *from, char_u *to)
7770{
7771 WCHAR *fromw;
7772 WCHAR *tow;
7773 HANDLE sh;
7774 WIN32_STREAM_ID sid;
7775 int headersize;
7776 WCHAR streamname[_MAX_PATH];
7777 DWORD readcount;
7778 void *context = NULL;
7779 DWORD lo, hi;
7780 int len;
7781
Bram Moolenaar0f873732019-12-05 20:28:46 +01007782 // Convert the file names to wide characters.
Bram Moolenaar36f692d2008-11-20 16:10:17 +00007783 fromw = enc_to_utf16(from, NULL);
7784 tow = enc_to_utf16(to, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007785 if (fromw != NULL && tow != NULL)
7786 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007787 // Open the file for reading.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007788 sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL,
7789 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
7790 if (sh != INVALID_HANDLE_VALUE)
7791 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007792 // Use BackupRead() to find the info streams. Repeat until we
7793 // have done them all.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007794 for (;;)
7795 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007796 // Get the header to find the length of the stream name. If
7797 // the "readcount" is zero we have done all info streams.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007798 ZeroMemory(&sid, sizeof(WIN32_STREAM_ID));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00007799 headersize = (int)((char *)&sid.cStreamName - (char *)&sid.dwStreamId);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007800 if (!BackupRead(sh, (LPBYTE)&sid, headersize,
7801 &readcount, FALSE, FALSE, &context)
7802 || readcount == 0)
7803 break;
7804
Bram Moolenaar0f873732019-12-05 20:28:46 +01007805 // We only deal with streams that have a name. The normal
7806 // file data appears to be without a name, even though docs
7807 // suggest it is called "::$DATA".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 if (sid.dwStreamNameSize > 0)
7809 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01007810 // Read the stream name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 if (!BackupRead(sh, (LPBYTE)streamname,
7812 sid.dwStreamNameSize,
7813 &readcount, FALSE, FALSE, &context))
7814 break;
7815
Bram Moolenaar0f873732019-12-05 20:28:46 +01007816 // Copy an info stream with a name ":anything:$DATA".
7817 // Skip "::$DATA", it has no stream name (examples suggest
7818 // it might be used for the normal file contents).
7819 // Note that BackupRead() counts bytes, but the name is in
7820 // wide characters.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821 len = readcount / sizeof(WCHAR);
7822 streamname[len] = 0;
7823 if (len > 7 && wcsicmp(streamname + len - 6,
7824 L":$DATA") == 0)
7825 {
7826 streamname[len - 6] = 0;
7827 copy_substream(sh, &context, tow, streamname,
Bram Moolenaarf4b8e572004-06-24 15:53:16 +00007828 (long)sid.Size.u.LowPart);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007829 }
7830 }
7831
Bram Moolenaar0f873732019-12-05 20:28:46 +01007832 // Advance to the next stream. We might try seeking too far,
7833 // but BackupSeek() doesn't skip over stream borders, thus
7834 // that's OK.
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00007835 (void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836 &lo, &hi, &context);
7837 }
7838
Bram Moolenaar0f873732019-12-05 20:28:46 +01007839 // Clear the context.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007840 (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context);
7841
7842 CloseHandle(sh);
7843 }
7844 }
7845 vim_free(fromw);
7846 vim_free(tow);
7847}
Bram Moolenaar071d4272004-06-13 20:20:40 +00007848
7849/*
Bram Moolenaare7bebc42021-02-01 20:50:37 +01007850 * ntdll.dll definitions
7851 */
7852#define FileEaInformation 7
7853#ifndef STATUS_SUCCESS
7854# define STATUS_SUCCESS ((NTSTATUS) 0x00000000L)
7855#endif
7856
7857typedef struct _FILE_FULL_EA_INFORMATION_ {
7858 ULONG NextEntryOffset;
7859 UCHAR Flags;
7860 UCHAR EaNameLength;
7861 USHORT EaValueLength;
7862 CHAR EaName[1];
7863} FILE_FULL_EA_INFORMATION_, *PFILE_FULL_EA_INFORMATION_;
7864
7865typedef struct _FILE_EA_INFORMATION_ {
7866 ULONG EaSize;
7867} FILE_EA_INFORMATION_, *PFILE_EA_INFORMATION_;
7868
Paul Ollis65745772022-06-05 16:55:54 +01007869#ifndef PROTO
Bram Moolenaare7bebc42021-02-01 20:50:37 +01007870typedef NTSTATUS (NTAPI *PfnNtOpenFile)(
7871 PHANDLE FileHandle,
7872 ACCESS_MASK DesiredAccess,
7873 POBJECT_ATTRIBUTES ObjectAttributes,
7874 PIO_STATUS_BLOCK IoStatusBlock,
7875 ULONG ShareAccess,
7876 ULONG OpenOptions);
7877typedef NTSTATUS (NTAPI *PfnNtClose)(
7878 HANDLE Handle);
7879typedef NTSTATUS (NTAPI *PfnNtSetEaFile)(
Bram Moolenaar9f1983d2022-05-12 20:35:35 +01007880 HANDLE FileHandle,
7881 PIO_STATUS_BLOCK IoStatusBlock,
7882 PVOID Buffer,
7883 ULONG Length);
Bram Moolenaare7bebc42021-02-01 20:50:37 +01007884typedef NTSTATUS (NTAPI *PfnNtQueryEaFile)(
7885 HANDLE FileHandle,
7886 PIO_STATUS_BLOCK IoStatusBlock,
7887 PVOID Buffer,
7888 ULONG Length,
7889 BOOLEAN ReturnSingleEntry,
7890 PVOID EaList,
7891 ULONG EaListLength,
7892 PULONG EaIndex,
7893 BOOLEAN RestartScan);
7894typedef NTSTATUS (NTAPI *PfnNtQueryInformationFile)(
Bram Moolenaar9f1983d2022-05-12 20:35:35 +01007895 HANDLE FileHandle,
7896 PIO_STATUS_BLOCK IoStatusBlock,
7897 PVOID FileInformation,
7898 ULONG Length,
Bram Moolenaare7bebc42021-02-01 20:50:37 +01007899 FILE_INFORMATION_CLASS FileInformationClass);
7900typedef VOID (NTAPI *PfnRtlInitUnicodeString)(
7901 PUNICODE_STRING DestinationString,
7902 PCWSTR SourceString);
7903
7904PfnNtOpenFile pNtOpenFile = NULL;
7905PfnNtClose pNtClose = NULL;
7906PfnNtSetEaFile pNtSetEaFile = NULL;
7907PfnNtQueryEaFile pNtQueryEaFile = NULL;
7908PfnNtQueryInformationFile pNtQueryInformationFile = NULL;
7909PfnRtlInitUnicodeString pRtlInitUnicodeString = NULL;
Paul Ollis65745772022-06-05 16:55:54 +01007910#endif
Bram Moolenaare7bebc42021-02-01 20:50:37 +01007911
7912/*
7913 * Load ntdll.dll functions.
7914 */
7915 static BOOL
7916load_ntdll(void)
7917{
7918 static int loaded = -1;
7919
7920 if (loaded == -1)
7921 {
7922 HMODULE hNtdll = GetModuleHandle("ntdll.dll");
7923 if (hNtdll != NULL)
7924 {
7925 pNtOpenFile = (PfnNtOpenFile) GetProcAddress(hNtdll, "NtOpenFile");
7926 pNtClose = (PfnNtClose) GetProcAddress(hNtdll, "NtClose");
7927 pNtSetEaFile = (PfnNtSetEaFile)
7928 GetProcAddress(hNtdll, "NtSetEaFile");
7929 pNtQueryEaFile = (PfnNtQueryEaFile)
7930 GetProcAddress(hNtdll, "NtQueryEaFile");
7931 pNtQueryInformationFile = (PfnNtQueryInformationFile)
7932 GetProcAddress(hNtdll, "NtQueryInformationFile");
7933 pRtlInitUnicodeString = (PfnRtlInitUnicodeString)
7934 GetProcAddress(hNtdll, "RtlInitUnicodeString");
7935 }
7936 if (pNtOpenFile == NULL
7937 || pNtClose == NULL
7938 || pNtSetEaFile == NULL
7939 || pNtQueryEaFile == NULL
7940 || pNtQueryInformationFile == NULL
7941 || pRtlInitUnicodeString == NULL)
7942 loaded = FALSE;
7943 else
7944 loaded = TRUE;
7945 }
7946 return (BOOL) loaded;
7947}
7948
7949/*
7950 * Copy extended attributes (EA) from file "from" to file "to".
7951 */
7952 static void
7953copy_extattr(char_u *from, char_u *to)
7954{
7955 char_u *fromf = NULL;
7956 char_u *tof = NULL;
7957 WCHAR *fromw = NULL;
7958 WCHAR *tow = NULL;
7959 UNICODE_STRING u;
7960 HANDLE h;
7961 OBJECT_ATTRIBUTES oa;
7962 IO_STATUS_BLOCK iosb;
7963 FILE_EA_INFORMATION_ eainfo = {0};
7964 void *ea = NULL;
7965
7966 if (!load_ntdll())
7967 return;
7968
7969 // Convert the file names to the fully qualified object names.
7970 fromf = alloc(STRLEN(from) + 5);
7971 tof = alloc(STRLEN(to) + 5);
7972 if (fromf == NULL || tof == NULL)
7973 goto theend;
7974 STRCPY(fromf, "\\??\\");
7975 STRCAT(fromf, from);
7976 STRCPY(tof, "\\??\\");
7977 STRCAT(tof, to);
7978
7979 // Convert the names to wide characters.
7980 fromw = enc_to_utf16(fromf, NULL);
7981 tow = enc_to_utf16(tof, NULL);
7982 if (fromw == NULL || tow == NULL)
7983 goto theend;
7984
7985 // Get the EA.
7986 pRtlInitUnicodeString(&u, fromw);
7987 InitializeObjectAttributes(&oa, &u, 0, NULL, NULL);
7988 if (pNtOpenFile(&h, FILE_READ_EA, &oa, &iosb, 0,
7989 FILE_NON_DIRECTORY_FILE) != STATUS_SUCCESS)
7990 goto theend;
7991 pNtQueryInformationFile(h, &iosb, &eainfo, sizeof(eainfo),
7992 FileEaInformation);
7993 if (eainfo.EaSize != 0)
7994 {
7995 ea = alloc(eainfo.EaSize);
7996 if (ea != NULL)
7997 {
7998 if (pNtQueryEaFile(h, &iosb, ea, eainfo.EaSize, FALSE,
7999 NULL, 0, NULL, TRUE) != STATUS_SUCCESS)
8000 {
8001 vim_free(ea);
8002 ea = NULL;
8003 }
8004 }
8005 }
8006 pNtClose(h);
8007
8008 // Set the EA.
8009 if (ea != NULL)
8010 {
8011 pRtlInitUnicodeString(&u, tow);
8012 InitializeObjectAttributes(&oa, &u, 0, NULL, NULL);
8013 if (pNtOpenFile(&h, FILE_WRITE_EA, &oa, &iosb, 0,
8014 FILE_NON_DIRECTORY_FILE) != STATUS_SUCCESS)
8015 goto theend;
8016
8017 pNtSetEaFile(h, &iosb, ea, eainfo.EaSize);
8018 pNtClose(h);
8019 }
8020
8021theend:
8022 vim_free(fromf);
8023 vim_free(tof);
8024 vim_free(fromw);
8025 vim_free(tow);
8026 vim_free(ea);
8027}
8028
8029/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008030 * Copy file attributes from file "from" to file "to".
8031 * For Windows NT and later we copy info streams.
8032 * Always returns zero, errors are ignored.
8033 */
8034 int
8035mch_copy_file_attribute(char_u *from, char_u *to)
8036{
Bram Moolenaar0f873732019-12-05 20:28:46 +01008037 // File streams only work on Windows NT and later.
Bram Moolenaarcea912a2016-10-12 14:20:24 +02008038 copy_infostreams(from, to);
Bram Moolenaare7bebc42021-02-01 20:50:37 +01008039 copy_extattr(from, to);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008040 return 0;
8041}
8042
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008043
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008044/*
K.Takatadc73b4b2021-06-08 18:32:36 +02008045 * The command line arguments in UTF-16
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008046 */
Bram Moolenaard857f0e2005-06-21 22:37:39 +00008047static int nArgsW = 0;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008048static LPWSTR *ArglistW = NULL;
8049static int global_argc = 0;
8050static char **global_argv;
8051
Bram Moolenaar0f873732019-12-05 20:28:46 +01008052static int used_file_argc = 0; // last argument in global_argv[] used
8053 // for the argument list.
8054static int *used_file_indexes = NULL; // indexes in global_argv[] for
8055 // command line arguments added to
8056 // the argument list
8057static int used_file_count = 0; // nr of entries in used_file_indexes
8058static int used_file_literal = FALSE; // take file names literally
8059static int used_file_full_path = FALSE; // file name was full path
8060static int used_file_diff_mode = FALSE; // file name was with diff mode
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008061static int used_alist_count = 0;
8062
8063
8064/*
8065 * Get the command line arguments. Unicode version.
8066 * Returns argc. Zero when something fails.
8067 */
8068 int
8069get_cmd_argsW(char ***argvp)
8070{
8071 char **argv = NULL;
8072 int argc = 0;
8073 int i;
8074
Bram Moolenaar14993322014-09-09 12:25:33 +02008075 free_cmd_argsW();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008076 ArglistW = CommandLineToArgvW(GetCommandLineW(), &nArgsW);
8077 if (ArglistW != NULL)
8078 {
8079 argv = malloc((nArgsW + 1) * sizeof(char *));
8080 if (argv != NULL)
8081 {
8082 argc = nArgsW;
8083 argv[argc] = NULL;
8084 for (i = 0; i < argc; ++i)
8085 {
8086 int len;
8087
K.Takatadc73b4b2021-06-08 18:32:36 +02008088 // Convert each Unicode argument to UTF-8.
8089 WideCharToMultiByte_alloc(CP_UTF8, 0,
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00008090 ArglistW[i], (int)wcslen(ArglistW[i]) + 1,
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008091 (LPSTR *)&argv[i], &len, 0, 0);
8092 if (argv[i] == NULL)
8093 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01008094 // Out of memory, clear everything.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008095 while (i > 0)
8096 free(argv[--i]);
8097 free(argv);
Bram Moolenaar73c61632013-12-07 14:48:10 +01008098 argv = NULL;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008099 argc = 0;
8100 }
8101 }
8102 }
8103 }
8104
8105 global_argc = argc;
8106 global_argv = argv;
8107 if (argc > 0)
Bram Moolenaar14993322014-09-09 12:25:33 +02008108 {
8109 if (used_file_indexes != NULL)
8110 free(used_file_indexes);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008111 used_file_indexes = malloc(argc * sizeof(int));
Bram Moolenaar14993322014-09-09 12:25:33 +02008112 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008113
8114 if (argvp != NULL)
8115 *argvp = argv;
8116 return argc;
8117}
8118
8119 void
8120free_cmd_argsW(void)
8121{
8122 if (ArglistW != NULL)
8123 {
8124 GlobalFree(ArglistW);
8125 ArglistW = NULL;
8126 }
8127}
8128
8129/*
8130 * Remember "name" is an argument that was added to the argument list.
8131 * This avoids that we have to re-parse the argument list when fix_arg_enc()
8132 * is called.
8133 */
8134 void
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008135used_file_arg(char *name, int literal, int full_path, int diff_mode)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008136{
8137 int i;
8138
8139 if (used_file_indexes == NULL)
8140 return;
8141 for (i = used_file_argc + 1; i < global_argc; ++i)
8142 if (STRCMP(global_argv[i], name) == 0)
8143 {
8144 used_file_argc = i;
8145 used_file_indexes[used_file_count++] = i;
8146 break;
8147 }
8148 used_file_literal = literal;
8149 used_file_full_path = full_path;
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008150 used_file_diff_mode = diff_mode;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008151}
8152
8153/*
8154 * Remember the length of the argument list as it was. If it changes then we
8155 * leave it alone when 'encoding' is set.
8156 */
8157 void
8158set_alist_count(void)
8159{
8160 used_alist_count = GARGCOUNT;
8161}
8162
8163/*
8164 * Fix the encoding of the command line arguments. Invoked when 'encoding'
K.Takatadc73b4b2021-06-08 18:32:36 +02008165 * has been changed while starting up. Use the UTF-16 command line arguments
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008166 * and convert them to 'encoding'.
8167 */
8168 void
8169fix_arg_enc(void)
8170{
8171 int i;
8172 int idx;
8173 char_u *str;
Bram Moolenaar86b68352004-12-27 21:59:20 +00008174 int *fnum_list;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008175
Bram Moolenaar0f873732019-12-05 20:28:46 +01008176 // Safety checks:
8177 // - if argument count differs between the wide and non-wide argument
8178 // list, something must be wrong.
8179 // - the file name arguments must have been located.
8180 // - the length of the argument list wasn't changed by the user.
Bram Moolenaard857f0e2005-06-21 22:37:39 +00008181 if (global_argc != nArgsW
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008182 || ArglistW == NULL
8183 || used_file_indexes == NULL
8184 || used_file_count == 0
8185 || used_alist_count != GARGCOUNT)
8186 return;
8187
Bram Moolenaar0f873732019-12-05 20:28:46 +01008188 // Remember the buffer numbers for the arguments.
Bram Moolenaarc799fe22019-05-28 23:08:19 +02008189 fnum_list = ALLOC_MULT(int, GARGCOUNT);
Bram Moolenaar86b68352004-12-27 21:59:20 +00008190 if (fnum_list == NULL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01008191 return; // out of memory
Bram Moolenaar86b68352004-12-27 21:59:20 +00008192 for (i = 0; i < GARGCOUNT; ++i)
8193 fnum_list[i] = GARGLIST[i].ae_fnum;
8194
Bram Moolenaar0f873732019-12-05 20:28:46 +01008195 // Clear the argument list. Make room for the new arguments.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008196 alist_clear(&global_alist);
8197 if (ga_grow(&global_alist.al_ga, used_file_count) == FAIL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01008198 return; // out of memory
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008199
8200 for (i = 0; i < used_file_count; ++i)
8201 {
8202 idx = used_file_indexes[i];
Bram Moolenaar36f692d2008-11-20 16:10:17 +00008203 str = utf16_to_enc(ArglistW[idx], NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008204 if (str != NULL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00008205 {
Bram Moolenaar39d21e32017-08-05 23:09:31 +02008206 int literal = used_file_literal;
8207
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008208#ifdef FEAT_DIFF
Bram Moolenaar0f873732019-12-05 20:28:46 +01008209 // When using diff mode may need to concatenate file name to
8210 // directory name. Just like it's done in main().
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008211 if (used_file_diff_mode && mch_isdir(str) && GARGCOUNT > 0
8212 && !mch_isdir(alist_name(&GARGLIST[0])))
8213 {
8214 char_u *r;
8215
8216 r = concat_fnames(str, gettail(alist_name(&GARGLIST[0])), TRUE);
8217 if (r != NULL)
8218 {
8219 vim_free(str);
8220 str = r;
8221 }
8222 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01008223#endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01008224 // Re-use the old buffer by renaming it. When not using literal
8225 // names it's done by alist_expand() below.
Bram Moolenaar86b68352004-12-27 21:59:20 +00008226 if (used_file_literal)
8227 buf_set_name(fnum_list[i], str);
8228
Bram Moolenaar0f873732019-12-05 20:28:46 +01008229 // Check backtick literal. backtick literal is already expanded in
8230 // main.c, so this part add str as literal.
Bram Moolenaar39d21e32017-08-05 23:09:31 +02008231 if (literal == FALSE)
8232 {
Bram Moolenaar116a0f82017-08-07 21:17:57 +02008233 size_t len = STRLEN(str);
8234
Bram Moolenaar39d21e32017-08-05 23:09:31 +02008235 if (len > 2 && *str == '`' && *(str + len - 1) == '`')
8236 literal = TRUE;
8237 }
8238 alist_add(&global_alist, str, literal ? 2 : 0);
Bram Moolenaar86b68352004-12-27 21:59:20 +00008239 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008240 }
8241
8242 if (!used_file_literal)
8243 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01008244 // Now expand wildcards in the arguments.
8245 // Temporarily add '(' and ')' to 'isfname'. These are valid
8246 // filename characters but are excluded from 'isfname' to make
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00008247 // "gf" work on a file name in parentheses (e.g.: see vim.h).
Bram Moolenaar0f873732019-12-05 20:28:46 +01008248 // Also, unset wildignore to not be influenced by this option.
8249 // The arguments specified in command-line should be kept even if
8250 // encoding options were changed.
Christian Brabandtfd916d62021-11-01 22:44:33 +00008251 // Use :legacy so that it also works when in Vim9 script.
8252 do_cmdline_cmd((char_u *)":legacy let g:SaVe_ISF = &isf|set isf+=(,)");
8253 do_cmdline_cmd((char_u *)":legacy let g:SaVe_WIG = &wig|set wig=");
Bram Moolenaar86b68352004-12-27 21:59:20 +00008254 alist_expand(fnum_list, used_alist_count);
Christian Brabandtfd916d62021-11-01 22:44:33 +00008255 do_cmdline_cmd(
8256 (char_u *)":legacy let &isf = g:SaVe_ISF|unlet g:SaVe_ISF");
8257 do_cmdline_cmd(
8258 (char_u *)":legacy let &wig = g:SaVe_WIG|unlet g:SaVe_WIG");
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008259 }
8260
Bram Moolenaar0f873732019-12-05 20:28:46 +01008261 // If wildcard expansion failed, we are editing the first file of the
8262 // arglist and there is no file name: Edit the first argument now.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008263 if (curwin->w_arg_idx == 0 && curbuf->b_fname == NULL)
8264 {
8265 do_cmdline_cmd((char_u *)":rewind");
Bram Moolenaar05268152021-11-18 18:53:45 +00008266 if (GARGCOUNT == 1 && used_file_full_path
8267 && vim_chdirfile(alist_name(&GARGLIST[0]), "drop") == OK)
8268 last_chdir_reason = "drop";
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008269 }
Bram Moolenaar86b68352004-12-27 21:59:20 +00008270
8271 set_alist_count();
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00008272}
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01008273
8274 int
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02008275mch_setenv(char *var, char *value, int x UNUSED)
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01008276{
8277 char_u *envbuf;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02008278 WCHAR *p;
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01008279
Bram Moolenaar964b3742019-05-24 18:54:09 +02008280 envbuf = alloc(STRLEN(var) + STRLEN(value) + 2);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01008281 if (envbuf == NULL)
8282 return -1;
8283
8284 sprintf((char *)envbuf, "%s=%s", var, value);
8285
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02008286 p = enc_to_utf16(envbuf, NULL);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01008287
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02008288 vim_free(envbuf);
8289 if (p == NULL)
8290 return -1;
8291 _wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01008292#ifdef libintl_wputenv
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02008293 libintl_wputenv(p);
Bram Moolenaara12a1612019-01-24 16:39:02 +01008294#endif
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02008295 // Unlike Un*x systems, we can free the string for _wputenv().
8296 vim_free(p);
Bram Moolenaar7c23d1d2017-02-01 13:14:16 +01008297
8298 return 0;
8299}
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008300
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008301/*
8302 * Support for 256 colors and 24-bit colors was added in Windows 10
8303 * version 1703 (Creators update).
8304 */
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008305#define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063)
8306
8307/*
8308 * Support for pseudo-console (ConPTY) was added in windows 10
Bram Moolenaar57da6982019-09-13 22:30:11 +02008309 * version 1809 (October 2018 update).
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008310 */
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008311#define CONPTY_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 17763)
Bram Moolenaar57da6982019-09-13 22:30:11 +02008312
8313/*
8314 * ConPTY differences between versions, need different logic.
8315 * version 1903 (May 2019 update).
8316 */
8317#define CONPTY_1903_BUILD MAKE_VER(10, 0, 18362)
8318
8319/*
Bram Moolenaar36e7a822019-11-13 21:49:24 +01008320 * version 1909 (November 2019 update).
8321 */
8322#define CONPTY_1909_BUILD MAKE_VER(10, 0, 18363)
8323
8324/*
Bram Moolenaar7ed8f592020-04-28 20:44:42 +02008325 * Stay ahead of the next update, and when it's done, fix this.
8326 * version ? (2020 update, temporarily use the build number of insider preview)
8327 */
8328#define CONPTY_NEXT_UPDATE_BUILD MAKE_VER(10, 0, 19587)
8329
8330/*
Bram Moolenaar57da6982019-09-13 22:30:11 +02008331 * Confirm until this version. Also the logic changes.
8332 * insider preview.
8333 */
Bram Moolenaar4c063dd2019-10-04 21:29:12 +02008334#define CONPTY_INSIDER_BUILD MAKE_VER(10, 0, 18995)
Bram Moolenaar57da6982019-09-13 22:30:11 +02008335
8336/*
8337 * Not stable now.
8338 */
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008339#define CONPTY_STABLE_BUILD MAKE_VER(10, 0, 32767) // T.B.D.
Christopher Plewright1140b512022-11-12 18:46:05 +00008340// Notes:
8341// Win 10 22H2 Final is build 19045, it's conpty is widely used.
8342// Strangely, 19045 is newer but is a lower build number than the 2020 insider
8343// preview which had a build 19587. And, not sure how stable that was?
8344// Win Server 2022 (May 10, 2022) is build 20348, its conpty is widely used.
8345// Win 11 starts from build 22000, even though the major version says 10!
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008346
8347 static void
8348vtp_flag_init(void)
8349{
8350 DWORD ver = get_build_number();
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008351#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008352 DWORD mode;
8353 HANDLE out;
8354
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008355# ifdef VIMDLL
8356 if (!gui.in_use)
8357# endif
8358 {
8359 out = GetStdHandle(STD_OUTPUT_HANDLE);
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008360
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008361 vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0;
8362 GetConsoleMode(out, &mode);
8363 mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
8364 if (SetConsoleMode(out, mode) == 0)
8365 vtp_working = 0;
8366 }
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008367#endif
8368
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008369 if (ver >= CONPTY_FIRST_SUPPORT_BUILD)
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008370 conpty_working = 1;
8371 if (ver >= CONPTY_STABLE_BUILD)
8372 conpty_stable = 1;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008373
Bram Moolenaar57da6982019-09-13 22:30:11 +02008374 if (ver <= CONPTY_INSIDER_BUILD)
8375 conpty_type = 3;
Bram Moolenaar36e7a822019-11-13 21:49:24 +01008376 if (ver <= CONPTY_1909_BUILD)
8377 conpty_type = 2;
Bram Moolenaar57da6982019-09-13 22:30:11 +02008378 if (ver <= CONPTY_1903_BUILD)
8379 conpty_type = 2;
8380 if (ver < CONPTY_FIRST_SUPPORT_BUILD)
8381 conpty_type = 1;
Bram Moolenaar7ed8f592020-04-28 20:44:42 +02008382
8383 if (ver >= CONPTY_NEXT_UPDATE_BUILD)
8384 conpty_fix_type = 1;
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008385}
8386
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008387#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) || defined(PROTO)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008388
8389 static void
8390vtp_init(void)
8391{
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02008392# ifdef FEAT_TERMGUICOLORS
Christopher Plewright38804d62022-11-09 23:55:52 +00008393 if (!vtp_working)
Yasuhiro Matsumotoe42c8da2022-09-01 11:31:45 +01008394 {
Christopher Plewright38804d62022-11-09 23:55:52 +00008395 CONSOLE_SCREEN_BUFFER_INFOEX csbi;
8396 csbi.cbSize = sizeof(csbi);
8397 GetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
8398 save_console_bg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_bg];
8399 save_console_fg_rgb = (guicolor_T)csbi.ColorTable[g_color_index_fg];
8400 store_console_bg_rgb = save_console_bg_rgb;
8401 store_console_fg_rgb = save_console_fg_rgb;
8402
Yasuhiro Matsumotoe42c8da2022-09-01 11:31:45 +01008403 COLORREF bg;
8404 bg = (COLORREF)csbi.ColorTable[g_color_index_bg];
8405 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
8406 default_console_color_bg = bg;
Christopher Plewright38804d62022-11-09 23:55:52 +00008407
8408 COLORREF fg;
8409 fg = (COLORREF)csbi.ColorTable[g_color_index_fg];
8410 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
8411 default_console_color_fg = fg;
Yasuhiro Matsumotoe42c8da2022-09-01 11:31:45 +01008412 }
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01008413# endif
Christopher Plewrightc8b126d2022-12-22 13:45:23 +00008414 use_alternate_screen_buffer = win10_22H2_or_later && p_rs && vtp_working
Bram Moolenaar9fca1332022-12-22 21:06:41 +00008415 && !mch_getenv("VIM_TERMINAL");
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008416 set_console_color_rgb();
8417}
8418
8419 static void
8420vtp_exit(void)
8421{
Bram Moolenaardf543822020-01-30 11:53:59 +01008422 restore_console_color_rgb();
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008423}
8424
Bram Moolenaar06b7b582020-05-30 17:49:25 +02008425 int
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008426vtp_printf(
8427 char *format,
8428 ...)
8429{
8430 char_u buf[100];
8431 va_list list;
8432 DWORD result;
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008433 int len;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008434
8435 va_start(list, format);
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008436 len = vim_vsnprintf((char *)buf, 100, (char *)format, list);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008437 va_end(list);
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008438 WriteConsoleA(g_hConOut, buf, (DWORD)len, &result, NULL);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008439 return (int)result;
8440}
8441
8442 static void
8443vtp_sgr_bulk(
8444 int arg)
8445{
8446 int args[1];
8447
8448 args[0] = arg;
8449 vtp_sgr_bulks(1, args);
8450}
8451
K.Takata6e1d31e2022-02-03 13:05:32 +00008452# define FAST256(x) \
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008453 if ((*p-- = "0123456789"[(n = x % 10)]) \
8454 && x >= 10 && (*p-- = "0123456789"[((m = x % 100) - n) / 10]) \
8455 && x >= 100 && (*p-- = "012"[((x & 0xff) - m) / 100]));
8456
K.Takata6e1d31e2022-02-03 13:05:32 +00008457# define FAST256CASE(x) \
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008458 case x: \
8459 FAST256(newargs[x - 1]);
8460
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008461 static void
8462vtp_sgr_bulks(
8463 int argc,
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008464 int *args)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008465{
K.Takata6e1d31e2022-02-03 13:05:32 +00008466# define MAXSGR 16
8467# define SGRBUFSIZE 2 + 4 * MAXSGR + 1 // '\033[' + SGR + 'm'
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008468 char_u buf[SGRBUFSIZE];
8469 char_u *p;
8470 int in, out;
8471 int newargs[16];
8472 static int sgrfgr = -1, sgrfgg, sgrfgb;
8473 static int sgrbgr = -1, sgrbgg, sgrbgb;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008474
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008475 if (argc == 0)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008476 {
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008477 sgrfgr = sgrbgr = -1;
K.Takatadf5320c2022-09-01 13:20:16 +01008478 vtp_printf("\033[m");
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008479 return;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008480 }
Bram Moolenaar4e5534f2020-04-30 20:59:57 +02008481
8482 in = out = 0;
8483 while (in < argc)
8484 {
8485 int s = args[in];
8486 int copylen = 1;
8487
8488 if (s == 38)
8489 {
8490 if (argc - in >= 5 && args[in + 1] == 2)
8491 {
8492 if (sgrfgr == args[in + 2] && sgrfgg == args[in + 3]
8493 && sgrfgb == args[in + 4])
8494 {
8495 in += 5;
8496 copylen = 0;
8497 }
8498 else
8499 {
8500 sgrfgr = args[in + 2];
8501 sgrfgg = args[in + 3];
8502 sgrfgb = args[in + 4];
8503 copylen = 5;
8504 }
8505 }
8506 else if (argc - in >= 3 && args[in + 1] == 5)
8507 {
8508 sgrfgr = -1;
8509 copylen = 3;
8510 }
8511 }
8512 else if (s == 48)
8513 {
8514 if (argc - in >= 5 && args[in + 1] == 2)
8515 {
8516 if (sgrbgr == args[in + 2] && sgrbgg == args[in + 3]
8517 && sgrbgb == args[in + 4])
8518 {
8519 in += 5;
8520 copylen = 0;
8521 }
8522 else
8523 {
8524 sgrbgr = args[in + 2];
8525 sgrbgg = args[in + 3];
8526 sgrbgb = args[in + 4];
8527 copylen = 5;
8528 }
8529 }
8530 else if (argc - in >= 3 && args[in + 1] == 5)
8531 {
8532 sgrbgr = -1;
8533 copylen = 3;
8534 }
8535 }
8536 else if (30 <= s && s <= 39)
8537 sgrfgr = -1;
8538 else if (90 <= s && s <= 97)
8539 sgrfgr = -1;
8540 else if (40 <= s && s <= 49)
8541 sgrbgr = -1;
8542 else if (100 <= s && s <= 107)
8543 sgrbgr = -1;
8544 else if (s == 0)
8545 sgrfgr = sgrbgr = -1;
8546
8547 while (copylen--)
8548 newargs[out++] = args[in++];
8549 }
8550
8551 p = &buf[sizeof(buf) - 1];
8552 *p-- = 'm';
8553
8554 switch (out)
8555 {
8556 int n, m;
8557 DWORD r;
8558
8559 FAST256CASE(16);
8560 *p-- = ';';
8561 FAST256CASE(15);
8562 *p-- = ';';
8563 FAST256CASE(14);
8564 *p-- = ';';
8565 FAST256CASE(13);
8566 *p-- = ';';
8567 FAST256CASE(12);
8568 *p-- = ';';
8569 FAST256CASE(11);
8570 *p-- = ';';
8571 FAST256CASE(10);
8572 *p-- = ';';
8573 FAST256CASE(9);
8574 *p-- = ';';
8575 FAST256CASE(8);
8576 *p-- = ';';
8577 FAST256CASE(7);
8578 *p-- = ';';
8579 FAST256CASE(6);
8580 *p-- = ';';
8581 FAST256CASE(5);
8582 *p-- = ';';
8583 FAST256CASE(4);
8584 *p-- = ';';
8585 FAST256CASE(3);
8586 *p-- = ';';
8587 FAST256CASE(2);
8588 *p-- = ';';
8589 FAST256CASE(1);
8590 *p-- = '[';
8591 *p = '\033';
8592 WriteConsoleA(g_hConOut, p, (DWORD)(&buf[SGRBUFSIZE] - p), &r, NULL);
8593 default:
8594 break;
8595 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008596}
8597
Bram Moolenaar06b7b582020-05-30 17:49:25 +02008598 static void
8599wt_init(void)
8600{
Christopher Plewright1140b512022-11-12 18:46:05 +00008601 wt_working = mch_getenv("WT_SESSION") != NULL;
Bram Moolenaar06b7b582020-05-30 17:49:25 +02008602}
8603
Bram Moolenaar8a938af2018-05-01 17:30:41 +02008604# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02008605 static int
8606ctermtoxterm(
8607 int cterm)
8608{
Bram Moolenaar9894e392018-05-05 14:29:06 +02008609 char_u r, g, b, idx;
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02008610
8611 cterm_color2rgb(cterm, &r, &g, &b, &idx);
8612 return (((int)r << 16) | ((int)g << 8) | (int)b);
8613}
Bram Moolenaar8a938af2018-05-01 17:30:41 +02008614# endif
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02008615
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008616 static void
8617set_console_color_rgb(void)
8618{
8619# ifdef FEAT_TERMGUICOLORS
K.Takata27b53be2022-09-18 12:25:49 +01008620 CONSOLE_SCREEN_BUFFER_INFOEX csbi;
Bram Moolenaara050b942019-12-02 21:35:31 +01008621 guicolor_T fg, bg;
8622 int ctermfg, ctermbg;
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008623
Christopher Plewright38804d62022-11-09 23:55:52 +00008624 if (!vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008625 return;
8626
Bram Moolenaara050b942019-12-02 21:35:31 +01008627 get_default_console_color(&ctermfg, &ctermbg, &fg, &bg);
8628
Christopher Plewright38804d62022-11-09 23:55:52 +00008629 if (p_tgc || t_colors >= 256)
Bram Moolenaar06b7b582020-05-30 17:49:25 +02008630 {
8631 term_fg_rgb_color(fg);
8632 term_bg_rgb_color(bg);
8633 return;
8634 }
8635
Christopher Plewright38804d62022-11-09 23:55:52 +00008636 if (!conpty_working)
8637 {
8638 fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg);
8639 bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008640
Christopher Plewright38804d62022-11-09 23:55:52 +00008641 csbi.cbSize = sizeof(csbi);
8642 GetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008643
Christopher Plewright38804d62022-11-09 23:55:52 +00008644 csbi.cbSize = sizeof(csbi);
8645 csbi.srWindow.Right += 1;
8646 csbi.srWindow.Bottom += 1;
8647 store_console_bg_rgb = csbi.ColorTable[g_color_index_bg];
8648 store_console_fg_rgb = csbi.ColorTable[g_color_index_fg];
8649 csbi.ColorTable[g_color_index_bg] = (COLORREF)bg;
8650 csbi.ColorTable[g_color_index_fg] = (COLORREF)fg;
8651 SetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
8652 }
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008653# endif
8654}
8655
Bram Moolenaara050b942019-12-02 21:35:31 +01008656# if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8657 void
8658get_default_console_color(
8659 int *cterm_fg,
8660 int *cterm_bg,
8661 guicolor_T *gui_fg,
8662 guicolor_T *gui_bg)
8663{
8664 int id;
8665 guicolor_T guifg = INVALCOLOR;
8666 guicolor_T guibg = INVALCOLOR;
8667 int ctermfg = 0;
8668 int ctermbg = 0;
Christopher Plewright38804d62022-11-09 23:55:52 +00008669 int dummynull = 0;
Bram Moolenaara050b942019-12-02 21:35:31 +01008670
8671 id = syn_name2id((char_u *)"Normal");
8672 if (id > 0 && p_tgc)
8673 syn_id2colors(id, &guifg, &guibg);
8674 if (guifg == INVALCOLOR)
8675 {
8676 ctermfg = -1;
8677 if (id > 0)
Christopher Plewright38804d62022-11-09 23:55:52 +00008678 syn_id2cterm_bg(id, &ctermfg, &dummynull);
8679 if (vtp_working)
8680 {
8681 cterm_normal_fg_gui_color = guifg =
8682 ctermfg != -1 ? ctermtoxterm(ctermfg) : INVALCOLOR;
8683 ctermfg = ctermfg < 0 ? 0 : ctermfg;
8684 }
8685 else
8686 {
8687 guifg = ctermfg != -1 ? ctermtoxterm(ctermfg)
Bram Moolenaara050b942019-12-02 21:35:31 +01008688 : default_console_color_fg;
Christopher Plewright38804d62022-11-09 23:55:52 +00008689 cterm_normal_fg_gui_color = guifg;
8690 ctermfg = ctermfg < 0 ? 0 : ctermfg;
8691 }
Bram Moolenaara050b942019-12-02 21:35:31 +01008692 }
8693 if (guibg == INVALCOLOR)
8694 {
8695 ctermbg = -1;
8696 if (id > 0)
Christopher Plewright38804d62022-11-09 23:55:52 +00008697 syn_id2cterm_bg(id, &dummynull, &ctermbg);
8698 if (vtp_working)
Yasuhiro Matsumotoe42c8da2022-09-01 11:31:45 +01008699 {
8700 cterm_normal_bg_gui_color = guibg =
Yasuhiro Matsumotoaa04e1b2022-05-07 14:09:19 +01008701 ctermbg != -1 ? ctermtoxterm(ctermbg) : INVALCOLOR;
Yasuhiro Matsumotoe42c8da2022-09-01 11:31:45 +01008702 if (ctermbg < 0)
8703 ctermbg = 0;
8704 }
K.Takatae53a0d42022-09-05 21:45:11 +01008705 else
8706 {
8707 guibg = ctermbg != -1 ? ctermtoxterm(ctermbg)
8708 : default_console_color_bg;
8709 cterm_normal_bg_gui_color = guibg;
8710 ctermbg = ctermbg < 0 ? 0 : ctermbg;
8711 }
Bram Moolenaara050b942019-12-02 21:35:31 +01008712 }
8713
8714 *cterm_fg = ctermfg;
8715 *cterm_bg = ctermbg;
8716 *gui_fg = guifg;
8717 *gui_bg = guibg;
8718}
8719# endif
8720
Bram Moolenaardf543822020-01-30 11:53:59 +01008721/*
8722 * Set the console colors to the original colors or the last set colors.
8723 */
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008724 static void
8725reset_console_color_rgb(void)
8726{
8727# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008728
Christopher Plewright38804d62022-11-09 23:55:52 +00008729 if (vtp_working)
Bram Moolenaar06b7b582020-05-30 17:49:25 +02008730 return;
8731
Christopher Plewright38804d62022-11-09 23:55:52 +00008732 CONSOLE_SCREEN_BUFFER_INFOEX csbi;
8733
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008734 csbi.cbSize = sizeof(csbi);
K.Takata27b53be2022-09-18 12:25:49 +01008735 GetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008736
8737 csbi.cbSize = sizeof(csbi);
8738 csbi.srWindow.Right += 1;
8739 csbi.srWindow.Bottom += 1;
Bram Moolenaardf543822020-01-30 11:53:59 +01008740 csbi.ColorTable[g_color_index_bg] = (COLORREF)store_console_bg_rgb;
8741 csbi.ColorTable[g_color_index_fg] = (COLORREF)store_console_fg_rgb;
K.Takata27b53be2022-09-18 12:25:49 +01008742 SetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaardf543822020-01-30 11:53:59 +01008743# endif
8744}
8745
8746/*
8747 * Set the console colors to the original colors.
8748 */
8749 static void
8750restore_console_color_rgb(void)
8751{
8752# ifdef FEAT_TERMGUICOLORS
Christopher Plewright38804d62022-11-09 23:55:52 +00008753 if (vtp_working)
8754 return;
8755
K.Takata27b53be2022-09-18 12:25:49 +01008756 CONSOLE_SCREEN_BUFFER_INFOEX csbi;
Bram Moolenaardf543822020-01-30 11:53:59 +01008757
8758 csbi.cbSize = sizeof(csbi);
K.Takata27b53be2022-09-18 12:25:49 +01008759 GetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaardf543822020-01-30 11:53:59 +01008760
8761 csbi.cbSize = sizeof(csbi);
8762 csbi.srWindow.Right += 1;
8763 csbi.srWindow.Bottom += 1;
Bram Moolenaarf6ceaf12018-08-30 17:47:05 +02008764 csbi.ColorTable[g_color_index_bg] = (COLORREF)save_console_bg_rgb;
8765 csbi.ColorTable[g_color_index_fg] = (COLORREF)save_console_fg_rgb;
K.Takata27b53be2022-09-18 12:25:49 +01008766 SetConsoleScreenBufferInfoEx(g_hConOut, &csbi);
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008767# endif
8768}
8769
8770 void
8771control_console_color_rgb(void)
8772{
Christopher Plewright38804d62022-11-09 23:55:52 +00008773 if (vtp_working)
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008774 set_console_color_rgb();
8775 else
8776 reset_console_color_rgb();
8777}
8778
8779 int
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008780use_vtp(void)
8781{
8782 return USE_VTP;
8783}
8784
Bram Moolenaarc5cd8852018-05-01 15:47:38 +02008785 int
8786is_term_win32(void)
8787{
8788 return T_NAME != NULL && STRCMP(T_NAME, "win32") == 0;
8789}
8790
Bram Moolenaaraa5df7e2019-02-03 14:53:10 +01008791 int
8792has_vtp_working(void)
8793{
8794 return vtp_working;
8795}
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008796
Bram Moolenaar6902c0e2019-02-16 14:07:37 +01008797#endif
8798
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008799 int
8800has_conpty_working(void)
8801{
8802 return conpty_working;
8803}
8804
8805 int
Bram Moolenaar57da6982019-09-13 22:30:11 +02008806get_conpty_type(void)
8807{
8808 return conpty_type;
8809}
8810
8811 int
Bram Moolenaard9ef1b82019-02-13 19:23:10 +01008812is_conpty_stable(void)
8813{
8814 return conpty_stable;
8815}
Bram Moolenaar78d21da2019-02-17 15:00:52 +01008816
Bram Moolenaar7ed8f592020-04-28 20:44:42 +02008817 int
8818get_conpty_fix_type(void)
8819{
8820 return conpty_fix_type;
8821}
8822
Bram Moolenaarafde13b2019-04-28 19:46:49 +02008823#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL) || defined(PROTO)
Bram Moolenaar78d21da2019-02-17 15:00:52 +01008824 void
8825resize_console_buf(void)
8826{
8827 CONSOLE_SCREEN_BUFFER_INFO csbi;
8828 COORD coord;
8829 SMALL_RECT newsize;
8830
8831 if (GetConsoleScreenBufferInfo(g_hConOut, &csbi))
8832 {
8833 coord.X = SRWIDTH(csbi.srWindow);
8834 coord.Y = SRHEIGHT(csbi.srWindow);
8835 SetConsoleScreenBufferSize(g_hConOut, coord);
8836
8837 newsize.Left = 0;
8838 newsize.Top = 0;
8839 newsize.Right = coord.X - 1;
8840 newsize.Bottom = coord.Y - 1;
8841 SetConsoleWindowInfo(g_hConOut, TRUE, &newsize);
8842
8843 SetConsoleScreenBufferSize(g_hConOut, coord);
8844 }
8845}
8846#endif
Martin Tournoij1a3e5742021-07-24 13:57:29 +02008847
8848 char *
8849GetWin32Error(void)
8850{
K.Takatad68b2fc2022-02-12 11:18:37 +00008851 static char *oldmsg = NULL;
Martin Tournoij1a3e5742021-07-24 13:57:29 +02008852 char *msg = NULL;
K.Takatad68b2fc2022-02-12 11:18:37 +00008853
Martin Tournoij1a3e5742021-07-24 13:57:29 +02008854 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
8855 NULL, GetLastError(), 0, (LPSTR)&msg, 0, NULL);
K.Takatad68b2fc2022-02-12 11:18:37 +00008856 if (oldmsg != NULL)
8857 LocalFree(oldmsg);
Martin Tournoij1a3e5742021-07-24 13:57:29 +02008858 if (msg != NULL)
8859 {
8860 // remove trailing \r\n
8861 char *pcrlf = strstr(msg, "\r\n");
8862 if (pcrlf != NULL)
8863 *pcrlf = '\0';
K.Takatad68b2fc2022-02-12 11:18:37 +00008864 oldmsg = msg;
Martin Tournoij1a3e5742021-07-24 13:57:29 +02008865 }
8866 return msg;
8867}
Paul Ollis65745772022-06-05 16:55:54 +01008868
8869#if defined(FEAT_RELTIME) || defined(PROTO)
8870static HANDLE timer_handle;
8871static int timer_active = FALSE;
8872
8873/*
8874 * Calls to start_timeout alternate the return value pointer between the two
8875 * entries in timeout_flags. If the previously active timeout is very close to
8876 * expiring when start_timeout() is called then a race condition means that the
8877 * set_flag() function may still be invoked after the previous timer is
8878 * deleted. Ping-ponging between the two flags prevents this causing 'fake'
8879 * timeouts.
8880 */
Bram Moolenaar155f2d12022-06-20 13:38:33 +01008881static sig_atomic_t timeout_flags[2];
8882static int timeout_flag_idx = 0;
8883static sig_atomic_t *timeout_flag = &timeout_flags[0];
Paul Ollis65745772022-06-05 16:55:54 +01008884
8885
8886 static void CALLBACK
Bram Moolenaar35d7a2f2022-06-09 20:53:54 +01008887set_flag(void *param, BOOLEAN unused2 UNUSED)
Paul Ollis65745772022-06-05 16:55:54 +01008888{
8889 int *timeout_flag = (int *)param;
8890
8891 *timeout_flag = TRUE;
8892}
8893
8894/*
8895 * Stop any active timeout.
8896 */
8897 void
8898stop_timeout(void)
8899{
8900 if (timer_active)
8901 {
Christopher Plewright2a46f812022-10-16 19:47:45 +01008902 BOOL ret = DeleteTimerQueueTimer(NULL, timer_handle, NULL);
Paul Ollis65745772022-06-05 16:55:54 +01008903 timer_active = FALSE;
8904 if (!ret && GetLastError() != ERROR_IO_PENDING)
8905 {
8906 semsg(_(e_could_not_clear_timeout_str), GetWin32Error());
8907 }
8908 }
8909 *timeout_flag = FALSE;
8910}
8911
8912/*
8913 * Start the timeout timer.
8914 *
8915 * The period is defined in milliseconds.
8916 *
8917 * The return value is a pointer to a flag that is initialised to 0. If the
8918 * timeout expires, the flag is set to 1. This will only return pointers to
8919 * static memory; i.e. any pointer returned by this function may always be
8920 * safely dereferenced.
8921 *
8922 * This function is not expected to fail, but if it does it still returns a
8923 * valid flag pointer; the flag will remain stuck at zero.
8924 */
Bram Moolenaar155f2d12022-06-20 13:38:33 +01008925 volatile sig_atomic_t *
Paul Ollis65745772022-06-05 16:55:54 +01008926start_timeout(long msec)
8927{
Paul Ollis65745772022-06-05 16:55:54 +01008928 BOOL ret;
8929
Bram Moolenaar1f30caf2022-06-19 14:36:35 +01008930 timeout_flag = &timeout_flags[timeout_flag_idx];
Paul Ollis65745772022-06-05 16:55:54 +01008931
8932 stop_timeout();
8933 ret = CreateTimerQueueTimer(
8934 &timer_handle, NULL, set_flag, timeout_flag,
8935 (DWORD)msec, 0, WT_EXECUTEDEFAULT);
8936 if (!ret)
8937 {
8938 semsg(_(e_could_not_set_timeout_str), GetWin32Error());
8939 }
8940 else
8941 {
Bram Moolenaar1f30caf2022-06-19 14:36:35 +01008942 timeout_flag_idx = (timeout_flag_idx + 1) % 2;
Paul Ollis65745772022-06-05 16:55:54 +01008943 timer_active = TRUE;
8944 *timeout_flag = FALSE;
8945 }
8946 return timeout_flag;
8947}
8948#endif