blob: 7a323f327b09dcdf80d3e57e9ed0b810b827111c [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/*
11 * os_mswin.c
12 *
Bram Moolenaarcf7164a2016-02-20 13:55:06 +010013 * Routines for Win32.
Bram Moolenaar071d4272004-06-13 20:20:40 +000014 */
15
Bram Moolenaar071d4272004-06-13 20:20:40 +000016#include "vim.h"
17
Bram Moolenaar071d4272004-06-13 20:20:40 +000018#include <sys/types.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000019#include <signal.h>
20#include <limits.h>
K.Takata56f587b2024-06-19 19:56:03 +020021
22// cproto fails on missing include files
Bram Moolenaar82881492012-11-20 16:53:39 +010023#ifndef PROTO
24# include <process.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000025# include <direct.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000026
Bram Moolenaar651fca82021-11-29 20:39:38 +000027# if !defined(FEAT_GUI_MSWIN)
Bram Moolenaar82881492012-11-20 16:53:39 +010028# include <shellapi.h>
29# endif
30
31# if defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)
32# include <dlgs.h>
Bram Moolenaarcea912a2016-10-12 14:20:24 +020033# include <winspool.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010034# include <commdlg.h>
Bram Moolenaarb04a98f2016-12-01 20:32:29 +010035# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +010036#endif // PROTO
Bram Moolenaar071d4272004-06-13 20:20:40 +000037
Bram Moolenaar071d4272004-06-13 20:20:40 +000038/*
39 * When generating prototypes for Win32 on Unix, these lines make the syntax
40 * errors disappear. They do not need to be correct.
41 */
42#ifdef PROTO
Bram Moolenaar912bc4a2019-12-01 18:58:11 +010043# define WINAPI
44# define WINBASEAPI
Bram Moolenaar071d4272004-06-13 20:20:40 +000045typedef int BOOL;
46typedef int CALLBACK;
47typedef int COLORREF;
48typedef int CONSOLE_CURSOR_INFO;
49typedef int COORD;
50typedef int DWORD;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +010051typedef int ENUMLOGFONTW;
Bram Moolenaar071d4272004-06-13 20:20:40 +000052typedef int HANDLE;
53typedef int HDC;
54typedef int HFONT;
55typedef int HICON;
56typedef int HWND;
57typedef int INPUT_RECORD;
Bram Moolenaard21e5bd2022-06-27 22:52:43 +010058typedef int INT_PTR;
Bram Moolenaar071d4272004-06-13 20:20:40 +000059typedef int KEY_EVENT_RECORD;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +010060typedef int LOGFONTW;
Bram Moolenaar071d4272004-06-13 20:20:40 +000061typedef int LPARAM;
62typedef int LPBOOL;
63typedef int LPCSTR;
64typedef int LPCWSTR;
Bram Moolenaarb9cdb372019-04-17 18:24:35 +020065typedef int LPDWORD;
Bram Moolenaar071d4272004-06-13 20:20:40 +000066typedef int LPSTR;
67typedef int LPTSTR;
Bram Moolenaarb9cdb372019-04-17 18:24:35 +020068typedef int LPVOID;
Bram Moolenaar071d4272004-06-13 20:20:40 +000069typedef int LPWSTR;
70typedef int LRESULT;
71typedef int MOUSE_EVENT_RECORD;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +010072typedef int NEWTEXTMETRICW;
Bram Moolenaar071d4272004-06-13 20:20:40 +000073typedef int PACL;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +020074typedef int PRINTDLGW;
Bram Moolenaar071d4272004-06-13 20:20:40 +000075typedef int PSECURITY_DESCRIPTOR;
76typedef int PSID;
77typedef int SECURITY_INFORMATION;
78typedef int SHORT;
79typedef int SMALL_RECT;
80typedef int TEXTMETRIC;
81typedef int UINT;
82typedef int WCHAR;
Bram Moolenaarc447d8d2018-12-18 21:56:28 +010083typedef int WNDENUMPROC;
Bram Moolenaar071d4272004-06-13 20:20:40 +000084typedef int WORD;
85typedef int WPARAM;
86typedef void VOID;
87#endif
88
Bram Moolenaar0f873732019-12-05 20:28:46 +010089// Record all output and all keyboard & mouse input
90// #define MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +000091
92#ifdef MCH_WRITE_DUMP
93FILE* fdDump = NULL;
94#endif
95
Bram Moolenaarafde13b2019-04-28 19:46:49 +020096#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000097extern char g_szOrigTitle[];
98#endif
99
100#ifdef FEAT_GUI
101extern HWND s_hwnd;
102#else
Bram Moolenaar0f873732019-12-05 20:28:46 +0100103static HWND s_hwnd = 0; // console window handle, set by GetConsoleHwnd()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104#endif
105
Bram Moolenaar509ce2a2016-03-11 22:52:15 +0100106#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar0f873732019-12-05 20:28:46 +0100107int WSInitialized = FALSE; // WinSock is initialized
Bram Moolenaarf12d9832016-01-29 21:11:25 +0100108#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200111#ifndef PROTO
112/*
113 * Save the instance handle of the exe/dll.
114 */
115 void
116SaveInst(HINSTANCE hInst)
117{
118 g_hinst = hInst;
119}
120#endif
121
Bram Moolenaar071d4272004-06-13 20:20:40 +0000122#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
123/*
124 * GUI version of mch_exit().
125 * Shut down and exit with status `r'
126 * Careful: mch_exit() may be called before mch_init()!
127 */
128 void
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200129mch_exit_g(int r)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000130{
Bram Moolenaar955f1982017-02-05 15:10:51 +0100131 exiting = TRUE;
132
Bram Moolenaar071d4272004-06-13 20:20:40 +0000133 display_errors();
134
Bram Moolenaar0f873732019-12-05 20:28:46 +0100135 ml_close_all(TRUE); // remove all memfiles
Bram Moolenaar071d4272004-06-13 20:20:40 +0000136
137# ifdef FEAT_OLE
138 UninitOLE();
139# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +0100140# ifdef FEAT_JOB_CHANNEL
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141 if (WSInitialized)
142 {
143 WSInitialized = FALSE;
144 WSACleanup();
145 }
146# endif
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100147# ifdef DYNAMIC_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148 dyn_libintl_end();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100149# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150
151 if (gui.in_use)
152 gui_exit(r);
Bram Moolenaar85c79d32007-02-20 01:59:20 +0000153
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100154# ifdef EXITFREE
Bram Moolenaar85c79d32007-02-20 01:59:20 +0000155 free_all_mem();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100156# endif
Bram Moolenaar85c79d32007-02-20 01:59:20 +0000157
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158 exit(r);
159}
160
Bram Moolenaar0f873732019-12-05 20:28:46 +0100161#endif // FEAT_GUI_MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +0000162
163
164/*
165 * Init the tables for toupper() and tolower().
166 */
167 void
168mch_early_init(void)
169{
170 int i;
171
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172 PlatformId();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000173
Bram Moolenaar0f873732019-12-05 20:28:46 +0100174 // Init the tables for toupper() and tolower()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175 for (i = 0; i < 256; ++i)
176 toupper_tab[i] = tolower_tab[i] = i;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100177 CharUpperBuff((LPSTR)toupper_tab, 256);
178 CharLowerBuff((LPSTR)tolower_tab, 256);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179}
180
181
182/*
183 * Return TRUE if the input comes from a terminal, FALSE otherwise.
184 */
185 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100186mch_input_isatty(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000187{
188#ifdef FEAT_GUI_MSWIN
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200189# ifdef VIMDLL
190 if (gui.in_use)
191# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +0100192 return TRUE; // GUI always has a tty
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200193#endif
194#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195 if (isatty(read_cmd_fd))
196 return TRUE;
197 return FALSE;
198#endif
199}
200
Bram Moolenaar071d4272004-06-13 20:20:40 +0000201/*
202 * mch_settitle(): set titlebar of our window
203 */
204 void
205mch_settitle(
206 char_u *title,
Bram Moolenaarbd67aac2019-09-21 23:09:04 +0200207 char_u *icon UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208{
Bram Moolenaar651fca82021-11-29 20:39:38 +0000209#ifdef FEAT_GUI_MSWIN
210# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200211 if (gui.in_use)
Bram Moolenaar651fca82021-11-29 20:39:38 +0000212# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200213 {
214 gui_mch_settitle(title, icon);
215 return;
216 }
Bram Moolenaar651fca82021-11-29 20:39:38 +0000217#endif
218#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219 if (title != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000220 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200221 WCHAR *wp = enc_to_utf16(title, NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000222
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200223 if (wp == NULL)
224 return;
225
226 SetConsoleTitleW(wp);
227 vim_free(wp);
228 return;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000229 }
Bram Moolenaar651fca82021-11-29 20:39:38 +0000230#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231}
232
233
234/*
235 * Restore the window/icon title.
236 * which is one of:
Bram Moolenaar40385db2018-08-07 22:31:44 +0200237 * SAVE_RESTORE_TITLE: Just restore title
238 * SAVE_RESTORE_ICON: Just restore icon (which we don't have)
239 * SAVE_RESTORE_BOTH: Restore title and icon (which we don't have)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000240 */
241 void
Bram Moolenaar1266d672017-02-01 13:43:36 +0100242mch_restore_title(int which UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243{
Bram Moolenaar651fca82021-11-29 20:39:38 +0000244#if !defined(FEAT_GUI_MSWIN) || defined(VIMDLL)
245# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200246 if (!gui.in_use)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100247# endif
Bram Moolenaar651fca82021-11-29 20:39:38 +0000248 SetConsoleTitle(g_szOrigTitle);
249#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250}
251
252
253/*
254 * Return TRUE if we can restore the title (we can)
255 */
256 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100257mch_can_restore_title(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258{
259 return TRUE;
260}
261
262
263/*
264 * Return TRUE if we can restore the icon title (we can't)
265 */
266 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100267mch_can_restore_icon(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268{
269 return FALSE;
270}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271
272
273/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000274 * Get absolute file name into buffer "buf" of length "len" bytes,
275 * turning all '/'s into '\\'s and getting the correct case of each component
276 * of the file name. Append a (back)slash to a directory name.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 * When 'shellslash' set do it the other way around.
278 * Return OK or FAIL.
279 */
280 int
281mch_FullName(
282 char_u *fname,
283 char_u *buf,
284 int len,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100285 int force UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000286{
287 int nResult = FAIL;
Bram Moolenaareae1b912019-05-09 15:12:55 +0200288 WCHAR *wname;
289 WCHAR wbuf[MAX_PATH];
290 char_u *cname = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291
Bram Moolenaareae1b912019-05-09 15:12:55 +0200292 wname = enc_to_utf16(fname, NULL);
293 if (wname != NULL && _wfullpath(wbuf, wname, MAX_PATH) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000294 {
Bram Moolenaareae1b912019-05-09 15:12:55 +0200295 cname = utf16_to_enc((short_u *)wbuf, NULL);
296 if (cname != NULL)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000297 {
Bram Moolenaareae1b912019-05-09 15:12:55 +0200298 vim_strncpy(buf, cname, len - 1);
299 nResult = OK;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000300 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000301 }
Bram Moolenaareae1b912019-05-09 15:12:55 +0200302 vim_free(wname);
303 vim_free(cname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000304
305#ifdef USE_FNAME_CASE
306 fname_case(buf, len);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000307#else
308 slash_adjust(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309#endif
310
311 return nResult;
312}
313
314
315/*
316 * Return TRUE if "fname" does not depend on the current directory.
317 */
318 int
319mch_isFullName(char_u *fname)
320{
Bram Moolenaar0ea74212020-12-11 20:10:50 +0100321 // A name like "d:/foo" and "//server/share" is absolute. "d:foo" is not.
322 // Another way to check is to use mch_FullName() and see if the result is
323 // the same as the name or mch_FullName() fails. However, this has quite a
324 // bit of overhead, so let's not do that.
Yegappan Lakshmanan6df0f272021-12-16 13:06:10 +0000325 if (*fname == NUL)
Yegappan Lakshmanan5a664fe2021-12-29 18:16:21 +0000326 return FALSE;
Bram Moolenaar0ea74212020-12-11 20:10:50 +0100327 return ((ASCII_ISALPHA(fname[0]) && fname[1] == ':'
328 && (fname[2] == '/' || fname[2] == '\\'))
329 || (fname[0] == fname[1] && (fname[0] == '/' || fname[0] == '\\')));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000330}
331
332/*
333 * Replace all slashes by backslashes.
334 * This used to be the other way around, but MS-DOS sometimes has problems
335 * with slashes (e.g. in a command name). We can't have mixed slashes and
336 * backslashes, because comparing file names will not work correctly. The
337 * commands that use a file name should try to avoid the need to type a
338 * backslash twice.
339 * When 'shellslash' set do it the other way around.
Bram Moolenaarb4f6a462015-10-13 19:43:17 +0200340 * When the path looks like a URL leave it unmodified.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 */
342 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100343slash_adjust(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000344{
Bram Moolenaarb4f6a462015-10-13 19:43:17 +0200345 if (path_with_url(p))
346 return;
Bram Moolenaar39d21e32017-08-05 23:09:31 +0200347
348 if (*p == '`')
349 {
Bram Moolenaar116a0f82017-08-07 21:17:57 +0200350 size_t len = STRLEN(p);
351
Bram Moolenaar0f873732019-12-05 20:28:46 +0100352 // don't replace backslash in backtick quoted strings
Bram Moolenaar39d21e32017-08-05 23:09:31 +0200353 if (len > 2 && *(p + len - 1) == '`')
354 return;
355 }
356
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +0000357 while (*p)
358 {
359 if (*p == psepcN)
360 *p = psepc;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100361 MB_PTR_ADV(p);
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +0000362 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000363}
364
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200365 static int
LemonBoy40fd7e62022-05-05 20:18:16 +0100366read_reparse_point(const WCHAR *name, char_u *buf, DWORD *buf_len)
367{
368 HANDLE h;
369 BOOL ok;
370
371 h = CreateFileW(name, FILE_READ_ATTRIBUTES,
372 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
373 OPEN_EXISTING,
374 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
375 NULL);
376 if (h == INVALID_HANDLE_VALUE)
377 return FAIL;
378
379 ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, *buf_len,
380 buf_len, NULL);
381 CloseHandle(h);
382
383 return ok ? OK : FAIL;
384}
385
LemonBoy40fd7e62022-05-05 20:18:16 +0100386 char_u *
387resolve_appexeclink(char_u *fname)
388{
Bram Moolenaar9f1983d2022-05-12 20:35:35 +0100389 DWORD attr = 0;
390 int idx;
391 WCHAR *p, *end, *wname;
LemonBoy40fd7e62022-05-05 20:18:16 +0100392 // The buffer size is arbitrarily chosen to be "big enough" (TM), the
393 // ceiling should be around 16k.
Bram Moolenaar9f1983d2022-05-12 20:35:35 +0100394 char_u buf[4096];
395 DWORD buf_len = sizeof(buf);
LemonBoy40fd7e62022-05-05 20:18:16 +0100396 REPARSE_DATA_BUFFER *rb = (REPARSE_DATA_BUFFER *)buf;
397
398 wname = enc_to_utf16(fname, NULL);
399 if (wname == NULL)
400 return NULL;
401
402 attr = GetFileAttributesW(wname);
403 if (attr == INVALID_FILE_ATTRIBUTES ||
404 (attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
405 {
406 vim_free(wname);
407 return NULL;
408 }
409
410 // The applinks are similar to symlinks but with a huge difference: they can
411 // only be executed, any other I/O operation on them is bound to fail with
412 // ERROR_FILE_NOT_FOUND even though the file exists.
413 if (read_reparse_point(wname, buf, &buf_len) == FAIL)
414 {
415 vim_free(wname);
416 return NULL;
417 }
418 vim_free(wname);
419
420 if (rb->ReparseTag != IO_REPARSE_TAG_APPEXECLINK)
421 return NULL;
422
423 // The (undocumented) reparse buffer contains a set of N null-terminated
424 // Unicode strings, the application path is stored in the third one.
425 if (rb->AppExecLinkReparseBuffer.StringCount < 3)
426 return NULL;
427
428 p = rb->AppExecLinkReparseBuffer.StringList;
429 end = p + rb->ReparseDataLength / sizeof(WCHAR);
430 for (idx = 0; p < end
431 && idx < (int)rb->AppExecLinkReparseBuffer.StringCount
432 && idx != 2; )
433 {
Yegappan Lakshmananebb01bd2022-06-08 15:14:09 +0100434 if (*p++ == L'\0')
LemonBoy40fd7e62022-05-05 20:18:16 +0100435 ++idx;
436 }
437
438 return utf16_to_enc(p, NULL);
439}
440
LemonBoy23c5ebe2024-06-18 20:43:51 +0200441// Use 64-bit stat functions.
442#undef stat
443#undef _stat
444#undef _wstat
445#undef _fstat
446#define stat _stat64
447#define _stat _stat64
448#define _wstat _wstat64
449#define _fstat _fstat64
450
451/*
452 * Implements lstat() and stat() that can handle symlinks properly.
453 */
454 static int
455mswin_stat_impl(const WCHAR *name, stat_T *stp, const int resolve)
456{
457 int n;
458 int fd;
459 BOOL is_symlink = FALSE;
460 HANDLE hFind, h;
461 DWORD attr = 0;
462 DWORD flag = 0;
463 WIN32_FIND_DATAW findDataW;
464
465#ifdef _UCRT
466 if (resolve)
467 // Universal CRT can handle symlinks properly.
468 return _wstat(name, stp);
469#endif
470
471 hFind = FindFirstFileW(name, &findDataW);
472 if (hFind != INVALID_HANDLE_VALUE)
473 {
474 attr = findDataW.dwFileAttributes;
475 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
476 && (findDataW.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
477 is_symlink = TRUE;
478 FindClose(hFind);
479 }
480
481 // Use the plain old stat() whenever it's possible.
482 if (!is_symlink)
483 return _wstat(name, stp);
484
485 if (!resolve && is_symlink)
486 flag = FILE_FLAG_OPEN_REPARSE_POINT;
487 if (attr & FILE_ATTRIBUTE_DIRECTORY)
488 flag |= FILE_FLAG_BACKUP_SEMANTICS;
489
490 h = CreateFileW(name, FILE_READ_ATTRIBUTES,
491 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, flag,
492 NULL);
493 if (h == INVALID_HANDLE_VALUE)
494 return -1;
495
496 fd = _open_osfhandle((intptr_t)h, _O_RDONLY);
497 n = _fstat(fd, (struct _stat *)stp);
498 if ((n == 0) && (attr & FILE_ATTRIBUTE_DIRECTORY))
499 stp->st_mode = (stp->st_mode & ~S_IFMT) | S_IFDIR;
500 _close(fd);
501
502 return n;
503}
504
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505/*
506 * stat() can't handle a trailing '/' or '\', remove it first.
LemonBoy23c5ebe2024-06-18 20:43:51 +0200507 * When 'resolve' is true behave as lstat() wrt symlinks.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000508 */
LemonBoy23c5ebe2024-06-18 20:43:51 +0200509 static int
510stat_impl(const char *name, stat_T *stp, const int resolve)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100512 // WinNT and later can use _MAX_PATH wide characters for a pathname, which
513 // means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
514 // UTF-8.
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100515 char_u buf[_MAX_PATH * 3 + 1];
John Marriott51f6a782025-05-04 21:35:36 +0200516 size_t buflen;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100517 char_u *p;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200518 WCHAR *wp;
519 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200521 vim_strncpy((char_u *)buf, (char_u *)name, sizeof(buf) - 1);
John Marriott51f6a782025-05-04 21:35:36 +0200522 buflen = STRLEN(buf);
523 p = buf + buflen;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524 if (p > buf)
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100525 MB_PTR_BACK(buf, p);
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100526
Bram Moolenaar0f873732019-12-05 20:28:46 +0100527 // Remove trailing '\\' except root path.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000528 if (p > buf && (*p == '\\' || *p == '/') && p[-1] != ':')
John Marriott51f6a782025-05-04 21:35:36 +0200529 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 *p = NUL;
John Marriott51f6a782025-05-04 21:35:36 +0200531 buflen = (size_t)(p - buf);
532 }
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100533
534 if ((buf[0] == '\\' && buf[1] == '\\') || (buf[0] == '/' && buf[1] == '/'))
535 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100536 // UNC root path must be followed by '\\'.
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100537 p = vim_strpbrk(buf + 2, (char_u *)"\\/");
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100538 if (p != NULL)
539 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100540 p = vim_strpbrk(p + 1, (char_u *)"\\/");
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100541 if (p == NULL)
John Marriott51f6a782025-05-04 21:35:36 +0200542 STRCPY(buf + buflen, "\\");
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100543 }
544 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000545
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200546 wp = enc_to_utf16(buf, NULL);
547 if (wp == NULL)
548 return -1;
549
LemonBoy23c5ebe2024-06-18 20:43:51 +0200550 n = mswin_stat_impl(wp, stp, resolve);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200551 vim_free(wp);
552 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000553}
554
LemonBoy23c5ebe2024-06-18 20:43:51 +0200555 int
556vim_lstat(const char *name, stat_T *stp)
557{
558 return stat_impl(name, stp, FALSE);
559}
560
561 int
562vim_stat(const char *name, stat_T *stp)
563{
564 return stat_impl(name, stp, TRUE);
565}
566
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200567#if (defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568 void
Bram Moolenaar26e86442020-05-17 14:06:16 +0200569mch_settmode(tmode_T tmode UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100571 // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572}
573
574 int
575mch_get_shellsize(void)
576{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100577 // never used
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578 return OK;
579}
580
581 void
582mch_set_shellsize(void)
583{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100584 // never used
Bram Moolenaar071d4272004-06-13 20:20:40 +0000585}
586
587/*
588 * Rows and/or Columns has changed.
589 */
590 void
591mch_new_shellsize(void)
592{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100593 // never used
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594}
595
596#endif
597
598/*
599 * We have no job control, so fake it by starting a new shell.
600 */
601 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100602mch_suspend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603{
604 suspend_shell();
605}
606
607#if defined(USE_MCH_ERRMSG) || defined(PROTO)
608
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200609# ifdef display_errors
610# undef display_errors
611# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612
613/*
614 * Display the saved error message(s).
615 */
616 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100617display_errors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618{
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200619# ifdef FEAT_GUI
Bram Moolenaar819ab822022-06-13 22:34:14 +0100620 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200622# ifdef VIMDLL
623 if (gui.in_use || gui.starting)
624# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 {
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200626 if (error_ga.ga_data != NULL)
627 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100628 // avoid putting up a message box with blanks only
Bram Moolenaar819ab822022-06-13 22:34:14 +0100629 for (p = (char_u *)error_ga.ga_data; *p; ++p)
Keith Thompson184f71c2024-01-04 21:19:04 +0100630 if (!SAFE_isspace(*p))
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200631 {
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100632 // Only use a dialog when not using --gui-dialog-file:
633 // write text to a file.
634 if (!gui_dialog_log((char_u *)"Errors", p))
635 (void)gui_mch_dialog(
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000636 gui.starting ? VIM_INFO :
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000637 VIM_ERROR,
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000638 gui.starting ? (char_u *)_("Message") :
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000639 (char_u *)_("Error"),
Bram Moolenaar819ab822022-06-13 22:34:14 +0100640 p, (char_u *)_("&Ok"),
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100641 1, NULL, FALSE);
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200642 break;
643 }
644 ga_clear(&error_ga);
645 }
646 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 }
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200648# endif
649# if !defined(FEAT_GUI) || defined(VIMDLL)
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +0100650 FlushFileBuffers(GetStdHandle(STD_ERROR_HANDLE));
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200651# endif
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +0100652}
653#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000654
655
656/*
657 * Return TRUE if "p" contain a wildcard that can be expanded by
658 * dos_expandpath().
659 */
660 int
661mch_has_exp_wildcard(char_u *p)
662{
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100663 for ( ; *p; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000664 {
665 if (vim_strchr((char_u *)"?*[", *p) != NULL
666 || (*p == '~' && p[1] != NUL))
667 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000668 }
669 return FALSE;
670}
671
672/*
673 * Return TRUE if "p" contain a wildcard or a "~1" kind of thing (could be a
674 * shortened file name).
675 */
676 int
677mch_has_wildcard(char_u *p)
678{
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100679 for ( ; *p; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000680 {
681 if (vim_strchr((char_u *)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100682#ifdef VIM_BACKTICK
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683 "?*$[`"
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100684#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685 "?*$["
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100686#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000687 , *p) != NULL
688 || (*p == '~' && p[1] != NUL))
689 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000690 }
691 return FALSE;
692}
693
694
695/*
696 * The normal _chdir() does not change the default drive. This one does.
697 * Returning 0 implies success; -1 implies failure.
698 */
699 int
700mch_chdir(char *path)
701{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200702 WCHAR *p;
703 int n;
704
Bram Moolenaar0f873732019-12-05 20:28:46 +0100705 if (path[0] == NUL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706 return -1;
707
Bram Moolenaara2974d72009-07-14 16:38:36 +0000708 if (p_verbose >= 5)
709 {
710 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100711 smsg("chdir(%s)", path);
Bram Moolenaara2974d72009-07-14 16:38:36 +0000712 verbose_leave();
713 }
Keith Thompson184f71c2024-01-04 21:19:04 +0100714 if (SAFE_isalpha(path[0]) && path[1] == ':') // has a drive name
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100716 // If we can change to the drive, skip that part of the path. If we
717 // can't then the current directory may be invalid, try using chdir()
718 // with the whole path.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 if (_chdrive(TOLOWER_ASC(path[0]) - 'a' + 1) == 0)
720 path += 2;
721 }
722
Bram Moolenaar0f873732019-12-05 20:28:46 +0100723 if (*path == NUL) // drive name only
Bram Moolenaar071d4272004-06-13 20:20:40 +0000724 return 0;
725
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200726 p = enc_to_utf16((char_u *)path, NULL);
727 if (p == NULL)
728 return -1;
Bram Moolenaar15d0a8c2004-09-06 17:44:46 +0000729
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200730 n = _wchdir(p);
731 vim_free(p);
732 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733}
734
735
Bram Moolenaarafde13b2019-04-28 19:46:49 +0200736#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737/*
738 * return non-zero if a character is available
739 */
740 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100741mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100743 // never used
Bram Moolenaar071d4272004-06-13 20:20:40 +0000744 return TRUE;
745}
Bram Moolenaare9c21ae2017-08-03 20:44:48 +0200746
747# if defined(FEAT_TERMINAL) || defined(PROTO)
748/*
749 * Check for any pending input or messages.
750 */
751 int
752mch_check_messages(void)
753{
Bram Moolenaar0f873732019-12-05 20:28:46 +0100754 // TODO: check for messages
Bram Moolenaare9c21ae2017-08-03 20:44:48 +0200755 return TRUE;
756}
757# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000758#endif
759
760
Bram Moolenaar071d4272004-06-13 20:20:40 +0000761#if defined(FEAT_LIBCALL) || defined(PROTO)
762/*
763 * Call a DLL routine which takes either a string or int param
764 * and returns an allocated string.
765 * Return OK if it worked, FAIL if not.
766 */
John Marriott51f6a782025-05-04 21:35:36 +0200767typedef char_u *(*MYSTRPROCSTR)(char_u *);
768typedef int (*MYSTRPROCINT)(char_u *);
769typedef char_u *(*MYINTPROCSTR)(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770typedef int (*MYINTPROCINT)(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000771
Bram Moolenaar071d4272004-06-13 20:20:40 +0000772/*
773 * Check if a pointer points to a valid NUL terminated string.
774 * Return the length of the string, including terminating NUL.
775 * Returns 0 for an invalid pointer, 1 for an empty string.
776 */
777 static size_t
778check_str_len(char_u *str)
779{
780 SYSTEM_INFO si;
781 MEMORY_BASIC_INFORMATION mbi;
782 size_t length = 0;
783 size_t i;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100784 const char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785
Bram Moolenaar0f873732019-12-05 20:28:46 +0100786 // get page size
Bram Moolenaar071d4272004-06-13 20:20:40 +0000787 GetSystemInfo(&si);
788
Bram Moolenaar0f873732019-12-05 20:28:46 +0100789 // get memory information
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +0000790 if (!VirtualQuery(str, &mbi, sizeof(mbi)))
791 return 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +0000793 // pre cast these (typing savers)
794 long_u dwStr = (long_u)str;
795 long_u dwBaseAddress = (long_u)mbi.BaseAddress;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000796
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +0000797 // get start address of page that str is on
798 long_u strPage = dwStr - (dwStr - dwBaseAddress) % si.dwPageSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +0000800 // get length from str to end of page
801 long_u pageLength = si.dwPageSize - (dwStr - strPage);
802
803 for (p = str; !IsBadReadPtr(p, (UINT)pageLength);
804 p += pageLength, pageLength = si.dwPageSize)
805 for (i = 0; i < pageLength; ++i, ++length)
806 if (p[i] == NUL)
807 return length + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808
809 return 0;
810}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200812/*
813 * Passed to do_in_runtimepath() to load a vim.ico file.
814 */
815 static void
816mch_icon_load_cb(char_u *fname, void *cookie)
817{
818 HANDLE *h = (HANDLE *)cookie;
819
820 *h = LoadImage(NULL,
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100821 (LPSTR)fname,
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200822 IMAGE_ICON,
823 64,
824 64,
825 LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
826}
827
828/*
829 * Try loading an icon file from 'runtimepath'.
830 */
831 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100832mch_icon_load(HANDLE *iconp)
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200833{
834 return do_in_runtimepath((char_u *)"bitmaps/vim.ico",
Bram Moolenaar7f8989d2016-03-12 22:11:39 +0100835 0, mch_icon_load_cb, iconp);
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200836}
837
LemonBoy9987fe82024-07-04 13:20:49 +0200838/*
839 * Fill the buffer 'buf' with 'len' random bytes.
840 * Returns FAIL if the OS PRNG is not available or something went wrong.
841 */
842 int
843mch_get_random(char_u *buf, int len)
844{
845 static int initialized = NOTDONE;
846 static HINSTANCE hInstLib;
847 static BOOL (WINAPI *pProcessPrng)(PUCHAR, ULONG);
848
849 if (initialized == NOTDONE)
850 {
851 hInstLib = vimLoadLib("bcryptprimitives.dll");
852 if (hInstLib != NULL)
853 pProcessPrng = (void *)GetProcAddress(hInstLib, "ProcessPrng");
854 if (hInstLib == NULL || pProcessPrng == NULL)
855 {
856 FreeLibrary(hInstLib);
857 initialized = FAIL;
858 }
859 else
860 initialized = OK;
861 }
862
863 if (initialized == FAIL)
864 return FAIL;
865
866 // According to the documentation this call cannot fail.
867 pProcessPrng(buf, len);
868
869 return OK;
870}
871
Bram Moolenaar071d4272004-06-13 20:20:40 +0000872 int
873mch_libcall(
874 char_u *libname,
875 char_u *funcname,
Bram Moolenaar0f873732019-12-05 20:28:46 +0100876 char_u *argstring, // NULL when using a argint
Bram Moolenaar071d4272004-06-13 20:20:40 +0000877 int argint,
Bram Moolenaar0f873732019-12-05 20:28:46 +0100878 char_u **string_result,// NULL when using number_result
Bram Moolenaar071d4272004-06-13 20:20:40 +0000879 int *number_result)
880{
881 HINSTANCE hinstLib;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882
883 BOOL fRunTimeLinkSuccess = FALSE;
884
885 // Get a handle to the DLL module.
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100886 hinstLib = vimLoadLib((char *)libname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000887
888 // If the handle is valid, try to get the function address.
889 if (hinstLib != NULL)
890 {
John Marriott51f6a782025-05-04 21:35:36 +0200891 char_u *retval_str = NULL;
892 int retval_int = 0;
893 size_t len;
894
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100895# ifdef HAVE_TRY_EXCEPT
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896 __try
897 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100898# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000899 if (argstring != NULL)
900 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100901 // Call with string argument
John Marriott51f6a782025-05-04 21:35:36 +0200902 if (string_result != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903 {
John Marriott51f6a782025-05-04 21:35:36 +0200904 MYSTRPROCSTR proc;
905
906 proc = (MYSTRPROCSTR)GetProcAddress(hinstLib, (LPCSTR)funcname);
907 if (proc != NULL)
908 {
909 fRunTimeLinkSuccess = TRUE;
910 retval_str = proc(argstring);
911 }
912 }
913 else
914 {
915 MYSTRPROCINT proc;
916
917 proc = (MYSTRPROCINT)GetProcAddress(hinstLib, (LPCSTR)funcname);
918 if (proc != NULL)
919 {
920 fRunTimeLinkSuccess = TRUE;
921 retval_int = proc(argstring);
922 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000923 }
924 }
925 else
926 {
Bram Moolenaar0f873732019-12-05 20:28:46 +0100927 // Call with number argument
John Marriott51f6a782025-05-04 21:35:36 +0200928 if (string_result != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000929 {
John Marriott51f6a782025-05-04 21:35:36 +0200930 MYINTPROCSTR proc;
931
932 proc = (MYINTPROCSTR)GetProcAddress(hinstLib, (LPCSTR)funcname);
933 if (proc != NULL)
934 {
935 fRunTimeLinkSuccess = TRUE;
936 retval_str = proc(argint);
937 }
938 }
939 else
940 {
941 MYINTPROCINT proc;
942
943 proc = (MYINTPROCINT)GetProcAddress(hinstLib, (LPCSTR)funcname);
944 if (proc != NULL)
945 {
946 fRunTimeLinkSuccess = TRUE;
947 retval_int = proc(argint);
948 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000949 }
950 }
951
952 // Save the string before we free the library.
953 // Assume that a "1" result is an illegal pointer.
954 if (string_result == NULL)
955 *number_result = retval_int;
956 else if (retval_str != NULL
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100957 && (len = check_str_len(retval_str)) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958 {
Bram Moolenaar18a4ba22019-05-24 19:39:03 +0200959 *string_result = alloc(len);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000960 if (*string_result != NULL)
961 mch_memmove(*string_result, retval_str, len);
962 }
963
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100964# ifdef HAVE_TRY_EXCEPT
Bram Moolenaar071d4272004-06-13 20:20:40 +0000965 }
966 __except(EXCEPTION_EXECUTE_HANDLER)
967 {
968 if (GetExceptionCode() == EXCEPTION_STACK_OVERFLOW)
K.Takatac351dc12022-01-24 11:24:08 +0000969 _resetstkoflw();
John Marriott51f6a782025-05-04 21:35:36 +0200970 fRunTimeLinkSuccess = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100972# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973
974 // Free the DLL module.
975 (void)FreeLibrary(hinstLib);
976 }
977
978 if (!fRunTimeLinkSuccess)
979 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000980 semsg(_(e_library_call_failed_for_str), funcname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000981 return FAIL;
982 }
983
984 return OK;
985}
986#endif
987
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988/*
989 * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
990 */
991 void
Bram Moolenaar1266d672017-02-01 13:43:36 +0100992DumpPutS(const char *psz UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000993{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +0100994#ifdef MCH_WRITE_DUMP
Bram Moolenaar071d4272004-06-13 20:20:40 +0000995 if (fdDump)
996 {
997 fputs(psz, fdDump);
998 if (psz[strlen(psz) - 1] != '\n')
999 fputc('\n', fdDump);
1000 fflush(fdDump);
1001 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001002#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003}
1004
1005#ifdef _DEBUG
1006
1007void __cdecl
1008Trace(
1009 char *pszFormat,
1010 ...)
1011{
1012 CHAR szBuff[2048];
1013 va_list args;
1014
1015 va_start(args, pszFormat);
1016 vsprintf(szBuff, pszFormat, args);
1017 va_end(args);
1018
1019 OutputDebugString(szBuff);
1020}
1021
1022#endif //_DEBUG
1023
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001024#if !defined(FEAT_GUI) || defined(VIMDLL) || defined(PROTO)
Bram Moolenaar0f873732019-12-05 20:28:46 +01001025extern HWND g_hWnd; // This is in os_win32.c.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001026
1027/*
1028 * Showing the printer dialog is tricky since we have no GUI
1029 * window to parent it. The following routines are needed to
1030 * get the window parenting and Z-order to work properly.
1031 */
1032 static void
1033GetConsoleHwnd(void)
1034{
Bram Moolenaar0f873732019-12-05 20:28:46 +01001035 // Skip if it's already set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036 if (s_hwnd != 0)
1037 return;
1038
Bram Moolenaar0f873732019-12-05 20:28:46 +01001039 // Window handle may have been found by init code (Windows NT only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 if (g_hWnd != 0)
1041 {
1042 s_hwnd = g_hWnd;
1043 return;
1044 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045
Bram Moolenaare1ed53f2019-02-12 23:12:37 +01001046 s_hwnd = GetConsoleWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001047}
Bram Moolenaar843ee412004-06-30 16:16:41 +00001048
1049/*
1050 * Console implementation of ":winpos".
1051 */
1052 int
1053mch_get_winpos(int *x, int *y)
1054{
1055 RECT rect;
1056
1057 GetConsoleHwnd();
1058 GetWindowRect(s_hwnd, &rect);
1059 *x = rect.left;
1060 *y = rect.top;
1061 return OK;
1062}
1063
1064/*
1065 * Console implementation of ":winpos x y".
1066 */
1067 void
1068mch_set_winpos(int x, int y)
1069{
1070 GetConsoleHwnd();
1071 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1072 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1073}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074#endif
1075
1076#if (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) || defined(PROTO)
1077
Bram Moolenaar0f873732019-12-05 20:28:46 +01001078//=================================================================
1079// Win32 printer stuff
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080
1081static HFONT prt_font_handles[2][2][2];
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001082static PRINTDLGW prt_dlg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001083static const int boldface[2] = {FW_REGULAR, FW_BOLD};
1084static TEXTMETRIC prt_tm;
1085static int prt_line_height;
1086static int prt_number_width;
1087static int prt_left_margin;
1088static int prt_right_margin;
1089static int prt_top_margin;
1090static char_u szAppName[] = TEXT("VIM");
1091static HWND hDlgPrint;
1092static int *bUserAbort = NULL;
1093static char_u *prt_name = NULL;
1094
Bram Moolenaar0f873732019-12-05 20:28:46 +01001095// Defines which are also in vim.rc.
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001096# define IDC_BOX1 400
1097# define IDC_PRINTTEXT1 401
1098# define IDC_PRINTTEXT2 402
1099# define IDC_PROGRESS 403
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001101 static BOOL
1102vimSetDlgItemText(HWND hDlg, int nIDDlgItem, char_u *s)
1103{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001104 WCHAR *wp;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001105 BOOL ret;
1106
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001107 wp = enc_to_utf16(s, NULL);
1108 if (wp == NULL)
1109 return FALSE;
1110
1111 ret = SetDlgItemTextW(hDlg, nIDDlgItem, wp);
1112 vim_free(wp);
1113 return ret;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001114}
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001115
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116/*
1117 * Convert BGR to RGB for Windows GDI calls
1118 */
1119 static COLORREF
1120swap_me(COLORREF colorref)
1121{
1122 int temp;
1123 char *ptr = (char *)&colorref;
1124
1125 temp = *(ptr);
1126 *(ptr ) = *(ptr + 2);
1127 *(ptr + 2) = temp;
1128 return colorref;
1129}
1130
K.Takatac351dc12022-01-24 11:24:08 +00001131 static INT_PTR CALLBACK
Bram Moolenaar1266d672017-02-01 13:43:36 +01001132PrintDlgProc(
1133 HWND hDlg,
1134 UINT message,
1135 WPARAM wParam UNUSED,
1136 LPARAM lParam UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137{
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001138# ifdef FEAT_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +00001139 NONCLIENTMETRICS nm;
1140 static HFONT hfont;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001141# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001142
1143 switch (message)
1144 {
1145 case WM_INITDIALOG:
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001146# ifdef FEAT_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147 nm.cbSize = sizeof(NONCLIENTMETRICS);
1148 if (SystemParametersInfo(
1149 SPI_GETNONCLIENTMETRICS,
1150 sizeof(NONCLIENTMETRICS),
1151 &nm,
1152 0))
1153 {
1154 char buff[MAX_PATH];
1155 int i;
1156
Bram Moolenaar0f873732019-12-05 20:28:46 +01001157 // Translate the dialog texts
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 hfont = CreateFontIndirect(&nm.lfMessageFont);
1159 for (i = IDC_PRINTTEXT1; i <= IDC_PROGRESS; i++)
1160 {
1161 SendDlgItemMessage(hDlg, i, WM_SETFONT, (WPARAM)hfont, 1);
1162 if (GetDlgItemText(hDlg,i, buff, sizeof(buff)))
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001163 vimSetDlgItemText(hDlg,i, (char_u *)_(buff));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001164 }
1165 SendDlgItemMessage(hDlg, IDCANCEL,
1166 WM_SETFONT, (WPARAM)hfont, 1);
1167 if (GetDlgItemText(hDlg,IDCANCEL, buff, sizeof(buff)))
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001168 vimSetDlgItemText(hDlg,IDCANCEL, (char_u *)_(buff));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001169 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001170# endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001171 SetWindowText(hDlg, (LPCSTR)szAppName);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 if (prt_name != NULL)
1173 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001174 vimSetDlgItemText(hDlg, IDC_PRINTTEXT2, (char_u *)prt_name);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001175 VIM_CLEAR(prt_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 }
1177 EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_CLOSE, MF_GRAYED);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001178# if !defined(FEAT_GUI) || defined(VIMDLL)
1179# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001180 if (!gui.in_use)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001181# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001182 BringWindowToTop(s_hwnd);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001183# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 return TRUE;
1185
1186 case WM_COMMAND:
1187 *bUserAbort = TRUE;
1188 EnableWindow(GetParent(hDlg), TRUE);
1189 DestroyWindow(hDlg);
1190 hDlgPrint = NULL;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001191# ifdef FEAT_GETTEXT
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192 DeleteObject(hfont);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001193# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194 return TRUE;
1195 }
1196 return FALSE;
1197}
1198
1199 static BOOL CALLBACK
Bram Moolenaar1266d672017-02-01 13:43:36 +01001200AbortProc(HDC hdcPrn UNUSED, int iCode UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201{
1202 MSG msg;
1203
K.Takatab7057bd2022-01-21 11:37:07 +00001204 while (!*bUserAbort && PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205 {
K.Takatab7057bd2022-01-21 11:37:07 +00001206 if (!hDlgPrint || !IsDialogMessageW(hDlgPrint, &msg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207 {
1208 TranslateMessage(&msg);
K.Takatab7057bd2022-01-21 11:37:07 +00001209 DispatchMessageW(&msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210 }
1211 }
1212 return !*bUserAbort;
1213}
1214
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001215# if !defined(FEAT_GUI) || defined(VIMDLL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216
Bram Moolenaar26fdd7d2011-11-30 13:42:44 +01001217 static UINT_PTR CALLBACK
Bram Moolenaar071d4272004-06-13 20:20:40 +00001218PrintHookProc(
1219 HWND hDlg, // handle to dialog box
1220 UINT uiMsg, // message identifier
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02001221 WPARAM wParam UNUSED, // message parameter
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222 LPARAM lParam // message parameter
1223 )
1224{
1225 HWND hwndOwner;
1226 RECT rc, rcDlg, rcOwner;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001227 PRINTDLGW *pPD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001229 if (uiMsg != WM_INITDIALOG)
1230 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001232 // Get the owner window and dialog box rectangles.
1233 if ((hwndOwner = GetParent(hDlg)) == NULL)
1234 hwndOwner = GetDesktopWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001236 GetWindowRect(hwndOwner, &rcOwner);
1237 GetWindowRect(hDlg, &rcDlg);
1238 CopyRect(&rc, &rcOwner);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001240 // Offset the owner and dialog box rectangles so that
1241 // right and bottom values represent the width and
1242 // height, and then offset the owner again to discard
1243 // space taken up by the dialog box.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001245 OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1246 OffsetRect(&rc, -rc.left, -rc.top);
1247 OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001249 // The new position is the sum of half the remaining
1250 // space and the owner's original position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001251
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001252 SetWindowPos(hDlg,
1253 HWND_TOP,
1254 rcOwner.left + (rc.right / 2),
1255 rcOwner.top + (rc.bottom / 2),
1256 0, 0, // ignores size arguments
1257 SWP_NOSIZE);
1258
1259 // tackle the printdlg copiesctrl problem
1260 pPD = (PRINTDLGW *)lParam;
1261 pPD->nCopies = (WORD)pPD->lCustData;
1262 SetDlgItemInt( hDlg, edt3, pPD->nCopies, FALSE );
1263 // Bring the window to top
1264 BringWindowToTop(GetParent(hDlg));
1265 SetForegroundWindow(hDlg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266
1267 return FALSE;
1268}
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001269# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270
1271 void
1272mch_print_cleanup(void)
1273{
1274 int pifItalic;
1275 int pifBold;
1276 int pifUnderline;
1277
1278 for (pifBold = 0; pifBold <= 1; pifBold++)
1279 for (pifItalic = 0; pifItalic <= 1; pifItalic++)
1280 for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
1281 DeleteObject(prt_font_handles[pifBold][pifItalic][pifUnderline]);
1282
1283 if (prt_dlg.hDC != NULL)
1284 DeleteDC(prt_dlg.hDC);
1285 if (!*bUserAbort)
1286 SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
1287}
1288
1289 static int
1290to_device_units(int idx, int dpi, int physsize, int offset, int def_number)
1291{
1292 int ret = 0;
1293 int u;
1294 int nr;
1295
1296 u = prt_get_unit(idx);
1297 if (u == PRT_UNIT_NONE)
1298 {
1299 u = PRT_UNIT_PERC;
1300 nr = def_number;
1301 }
1302 else
1303 nr = printer_opts[idx].number;
1304
1305 switch (u)
1306 {
1307 case PRT_UNIT_PERC:
1308 ret = (physsize * nr) / 100;
1309 break;
1310 case PRT_UNIT_INCH:
1311 ret = (nr * dpi);
1312 break;
1313 case PRT_UNIT_MM:
1314 ret = (nr * 10 * dpi) / 254;
1315 break;
1316 case PRT_UNIT_POINT:
1317 ret = (nr * 10 * dpi) / 720;
1318 break;
1319 }
1320
1321 if (ret < offset)
1322 return 0;
1323 else
1324 return ret - offset;
1325}
1326
1327 static int
1328prt_get_cpl(void)
1329{
1330 int hr;
1331 int phyw;
1332 int dvoff;
1333 int rev_offset;
1334 int dpi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001335
1336 GetTextMetrics(prt_dlg.hDC, &prt_tm);
1337 prt_line_height = prt_tm.tmHeight + prt_tm.tmExternalLeading;
1338
1339 hr = GetDeviceCaps(prt_dlg.hDC, HORZRES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340 phyw = GetDeviceCaps(prt_dlg.hDC, PHYSICALWIDTH);
1341 dvoff = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001342 dpi = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSX);
1343
1344 rev_offset = phyw - (dvoff + hr);
1345
1346 prt_left_margin = to_device_units(OPT_PRINT_LEFT, dpi, phyw, dvoff, 10);
1347 if (prt_use_number())
1348 {
1349 prt_number_width = PRINT_NUMBER_WIDTH * prt_tm.tmAveCharWidth;
1350 prt_left_margin += prt_number_width;
1351 }
1352 else
1353 prt_number_width = 0;
1354
1355 prt_right_margin = hr - to_device_units(OPT_PRINT_RIGHT, dpi, phyw,
1356 rev_offset, 5);
1357
1358 return (prt_right_margin - prt_left_margin) / prt_tm.tmAveCharWidth;
1359}
1360
1361 static int
1362prt_get_lpp(void)
1363{
1364 int vr;
1365 int phyw;
1366 int dvoff;
1367 int rev_offset;
1368 int bottom_margin;
1369 int dpi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370
1371 vr = GetDeviceCaps(prt_dlg.hDC, VERTRES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372 phyw = GetDeviceCaps(prt_dlg.hDC, PHYSICALHEIGHT);
1373 dvoff = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETY);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374 dpi = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSY);
1375
1376 rev_offset = phyw - (dvoff + vr);
1377
1378 prt_top_margin = to_device_units(OPT_PRINT_TOP, dpi, phyw, dvoff, 5);
1379
Bram Moolenaar0f873732019-12-05 20:28:46 +01001380 // adjust top margin if there is a header
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 prt_top_margin += prt_line_height * prt_header_height();
1382
1383 bottom_margin = vr - to_device_units(OPT_PRINT_BOT, dpi, phyw,
1384 rev_offset, 5);
1385
1386 return (bottom_margin - prt_top_margin) / prt_line_height;
1387}
1388
1389 int
1390mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
1391{
1392 static HGLOBAL stored_dm = NULL;
1393 static HGLOBAL stored_devn = NULL;
1394 static int stored_nCopies = 1;
1395 static int stored_nFlags = 0;
1396
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001397 LOGFONTW fLogFont;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 int pifItalic;
1399 int pifBold;
1400 int pifUnderline;
1401
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001402 DEVMODEW *mem;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 DEVNAMES *devname;
1404 int i;
Bram Moolenaarb391e1f2023-01-16 19:51:03 +00001405 DWORD err;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406
1407 bUserAbort = &(psettings->user_abort);
Bram Moolenaara80faa82020-04-12 19:37:17 +02001408 CLEAR_FIELD(prt_dlg);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001409 prt_dlg.lStructSize = sizeof(PRINTDLGW);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001410# if !defined(FEAT_GUI) || defined(VIMDLL)
1411# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001412 if (!gui.in_use)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001413# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01001414 GetConsoleHwnd(); // get value of s_hwnd
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001415# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416 prt_dlg.hwndOwner = s_hwnd;
1417 prt_dlg.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
1418 if (!forceit)
1419 {
1420 prt_dlg.hDevMode = stored_dm;
1421 prt_dlg.hDevNames = stored_devn;
1422 prt_dlg.lCustData = stored_nCopies; // work around bug in print dialog
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001423# if !defined(FEAT_GUI) || defined(VIMDLL)
1424# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001425 if (!gui.in_use)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001426# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001427 {
1428 /*
1429 * Use hook to prevent console window being sent to back
1430 */
1431 prt_dlg.lpfnPrintHook = PrintHookProc;
1432 prt_dlg.Flags |= PD_ENABLEPRINTHOOK;
1433 }
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001434# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435 prt_dlg.Flags |= stored_nFlags;
1436 }
1437
1438 /*
1439 * If bang present, return default printer setup with no dialog
1440 * never show dialog if we are running over telnet
1441 */
1442 if (forceit
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001443# if !defined(FEAT_GUI) || defined(VIMDLL)
1444# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001445 || (!gui.in_use && !term_console)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001446# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447 || !term_console
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001448# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001449# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450 )
1451 {
1452 prt_dlg.Flags |= PD_RETURNDEFAULT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453 /*
1454 * MSDN suggests setting the first parameter to WINSPOOL for
1455 * NT, but NULL appears to work just as well.
1456 */
1457 if (*p_pdev != NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001458 prt_dlg.hDC = CreateDC(NULL, (LPCSTR)p_pdev, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001460 {
1461 prt_dlg.Flags |= PD_RETURNDEFAULT;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001462 if (PrintDlgW(&prt_dlg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001463 goto init_fail_dlg;
1464 }
1465 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001466 else if (PrintDlgW(&prt_dlg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467 goto init_fail_dlg;
1468 else
1469 {
1470 /*
1471 * keep the previous driver context
1472 */
1473 stored_dm = prt_dlg.hDevMode;
1474 stored_devn = prt_dlg.hDevNames;
1475 stored_nFlags = prt_dlg.Flags;
1476 stored_nCopies = prt_dlg.nCopies;
1477 }
1478
1479 if (prt_dlg.hDC == NULL)
1480 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00001481 emsg(_(e_printer_selection_failed));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001482 mch_print_cleanup();
1483 return FALSE;
1484 }
1485
Bram Moolenaar0f873732019-12-05 20:28:46 +01001486 // Not all printer drivers report the support of color (or grey) in the
1487 // same way. Let's set has_color if there appears to be some way to print
1488 // more than B&W.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 i = GetDeviceCaps(prt_dlg.hDC, NUMCOLORS);
1490 psettings->has_color = (GetDeviceCaps(prt_dlg.hDC, BITSPIXEL) > 1
1491 || GetDeviceCaps(prt_dlg.hDC, PLANES) > 1
1492 || i > 2 || i == -1);
1493
Bram Moolenaar0f873732019-12-05 20:28:46 +01001494 // Ensure all font styles are baseline aligned
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 SetTextAlign(prt_dlg.hDC, TA_BASELINE|TA_LEFT);
1496
1497 /*
1498 * On some windows systems the nCopies parameter is not
1499 * passed back correctly. It must be retrieved from the
1500 * hDevMode struct.
1501 */
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001502 mem = (DEVMODEW *)GlobalLock(prt_dlg.hDevMode);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 if (mem != NULL)
1504 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001505 if (mem->dmCopies != 1)
1506 stored_nCopies = mem->dmCopies;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 if ((mem->dmFields & DM_DUPLEX) && (mem->dmDuplex & ~DMDUP_SIMPLEX))
1508 psettings->duplex = TRUE;
1509 if ((mem->dmFields & DM_COLOR) && (mem->dmColor & DMCOLOR_COLOR))
1510 psettings->has_color = TRUE;
1511 }
1512 GlobalUnlock(prt_dlg.hDevMode);
1513
1514 devname = (DEVNAMES *)GlobalLock(prt_dlg.hDevNames);
1515 if (devname != 0)
1516 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001517 WCHAR *wprinter_name = (WCHAR *)devname + devname->wDeviceOffset;
1518 WCHAR *wport_name = (WCHAR *)devname + devname->wOutputOffset;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001519 char_u *text = (char_u *)_("to %s on %s");
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001520 char_u *printer_name = utf16_to_enc(wprinter_name, NULL);
1521 char_u *port_name = utf16_to_enc(wport_name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001522
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001523 if (printer_name != NULL && port_name != NULL)
Bram Moolenaar964b3742019-05-24 18:54:09 +02001524 prt_name = alloc(STRLEN(printer_name)
1525 + STRLEN(port_name) + STRLEN(text));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 if (prt_name != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001527 wsprintf((char *)prt_name, (const char *)text,
1528 printer_name, port_name);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001529 vim_free(printer_name);
1530 vim_free(port_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531 }
1532 GlobalUnlock(prt_dlg.hDevNames);
1533
1534 /*
1535 * Initialise the font according to 'printfont'
1536 */
Bram Moolenaara80faa82020-04-12 19:37:17 +02001537 CLEAR_FIELD(fLogFont);
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001538 if (get_logfont(&fLogFont, p_pfn, prt_dlg.hDC, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 {
Bram Moolenaard88be5b2022-01-04 19:57:55 +00001540 semsg(_(e_unknown_printer_font_str), p_pfn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 mch_print_cleanup();
1542 return FALSE;
1543 }
1544
1545 for (pifBold = 0; pifBold <= 1; pifBold++)
1546 for (pifItalic = 0; pifItalic <= 1; pifItalic++)
1547 for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
1548 {
1549 fLogFont.lfWeight = boldface[pifBold];
1550 fLogFont.lfItalic = pifItalic;
1551 fLogFont.lfUnderline = pifUnderline;
1552 prt_font_handles[pifBold][pifItalic][pifUnderline]
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001553 = CreateFontIndirectW(&fLogFont);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 }
1555
1556 SetBkMode(prt_dlg.hDC, OPAQUE);
1557 SelectObject(prt_dlg.hDC, prt_font_handles[0][0][0]);
1558
1559 /*
1560 * Fill in the settings struct
1561 */
1562 psettings->chars_per_line = prt_get_cpl();
1563 psettings->lines_per_page = prt_get_lpp();
Bram Moolenaar7ddc6422014-09-27 11:18:19 +02001564 if (prt_dlg.Flags & PD_USEDEVMODECOPIESANDCOLLATE)
1565 {
1566 psettings->n_collated_copies = (prt_dlg.Flags & PD_COLLATE)
1567 ? prt_dlg.nCopies : 1;
1568 psettings->n_uncollated_copies = (prt_dlg.Flags & PD_COLLATE)
1569 ? 1 : prt_dlg.nCopies;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001570
Bram Moolenaar7ddc6422014-09-27 11:18:19 +02001571 if (psettings->n_collated_copies == 0)
1572 psettings->n_collated_copies = 1;
1573
1574 if (psettings->n_uncollated_copies == 0)
1575 psettings->n_uncollated_copies = 1;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01001576 }
1577 else
1578 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001579 psettings->n_collated_copies = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 psettings->n_uncollated_copies = 1;
Bram Moolenaar7ddc6422014-09-27 11:18:19 +02001581 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582
1583 psettings->jobname = jobname;
1584
1585 return TRUE;
1586
1587init_fail_dlg:
Bram Moolenaarb391e1f2023-01-16 19:51:03 +00001588 err = CommDlgExtendedError();
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001589 if (err)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001590 {
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001591 char_u *buf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001593 // I suspect FormatMessage() doesn't work for values returned by
1594 // CommDlgExtendedError(). What does?
1595 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
1596 FORMAT_MESSAGE_FROM_SYSTEM |
1597 FORMAT_MESSAGE_IGNORE_INSERTS,
1598 NULL, err, 0, (LPTSTR)(&buf), 0, NULL);
1599 semsg(_(e_print_error_str),
1600 buf == NULL ? (char_u *)_("Unknown") : buf);
1601 LocalFree((LPVOID)(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001602 }
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00001603 else
1604 msg_clr_eos(); // Maybe canceled
1605
1606 mch_print_cleanup();
1607 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001608}
1609
1610
1611 int
1612mch_print_begin(prt_settings_T *psettings)
1613{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001614 int ret = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615 char szBuffer[300];
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001616 WCHAR *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001617
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001618 hDlgPrint = CreateDialog(g_hinst, TEXT("PrintDlgBox"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001619 prt_dlg.hwndOwner, PrintDlgProc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620 SetAbortProc(prt_dlg.hDC, AbortProc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001621 wsprintf(szBuffer, _("Printing '%s'"), gettail(psettings->jobname));
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001622 vimSetDlgItemText(hDlgPrint, IDC_PRINTTEXT1, (char_u *)szBuffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001623
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001624 wp = enc_to_utf16(psettings->jobname, NULL);
Bram Moolenaar2290b1f2018-05-13 17:30:45 +02001625 if (wp != NULL)
1626 {
1627 DOCINFOW di;
1628
Bram Moolenaara80faa82020-04-12 19:37:17 +02001629 CLEAR_FIELD(di);
Bram Moolenaar2290b1f2018-05-13 17:30:45 +02001630 di.cbSize = sizeof(di);
1631 di.lpszDocName = wp;
1632 ret = StartDocW(prt_dlg.hDC, &di);
1633 vim_free(wp);
1634 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001635
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001636# ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01001637 // Give focus back to main window (when using MDI).
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001638# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001639 if (gui.in_use)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001640# endif
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001641 SetFocus(s_hwnd);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001642# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001643
1644 return (ret > 0);
1645}
1646
1647 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01001648mch_print_end(prt_settings_T *psettings UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001649{
1650 EndDoc(prt_dlg.hDC);
1651 if (!*bUserAbort)
1652 SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
1653}
1654
1655 int
1656mch_print_end_page(void)
1657{
1658 return (EndPage(prt_dlg.hDC) > 0);
1659}
1660
1661 int
1662mch_print_begin_page(char_u *msg)
1663{
1664 if (msg != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001665 vimSetDlgItemText(hDlgPrint, IDC_PROGRESS, msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 return (StartPage(prt_dlg.hDC) > 0);
1667}
1668
1669 int
1670mch_print_blank_page(void)
1671{
1672 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
1673}
1674
1675static int prt_pos_x = 0;
1676static int prt_pos_y = 0;
1677
1678 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001679mch_print_start_line(int margin, int page_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680{
1681 if (margin)
1682 prt_pos_x = -prt_number_width;
1683 else
1684 prt_pos_x = 0;
1685 prt_pos_y = page_line * prt_line_height
1686 + prt_tm.tmAscent + prt_tm.tmExternalLeading;
1687}
1688
1689 int
1690mch_print_text_out(char_u *p, int len)
1691{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692 SIZE sz;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001693 WCHAR *wp;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001694 int wlen = len;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001695 int ret = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001696
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001697 wp = enc_to_utf16(p, &wlen);
1698 if (wp == NULL)
1699 return FALSE;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001700
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001701 TextOutW(prt_dlg.hDC, prt_pos_x + prt_left_margin,
1702 prt_pos_y + prt_top_margin, wp, wlen);
1703 GetTextExtentPoint32W(prt_dlg.hDC, wp, wlen, &sz);
1704 vim_free(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705 prt_pos_x += (sz.cx - prt_tm.tmOverhang);
Bram Moolenaar0f873732019-12-05 20:28:46 +01001706 // This is wrong when printing spaces for a TAB.
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001707 if (p[len] != NUL)
1708 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02001709 wlen = mb_ptr2len(p + len);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001710 wp = enc_to_utf16(p + len, &wlen);
1711 if (wp != NULL)
1712 {
1713 GetTextExtentPoint32W(prt_dlg.hDC, wp, 1, &sz);
1714 ret = (prt_pos_x + prt_left_margin + sz.cx > prt_right_margin);
1715 vim_free(wp);
1716 }
1717 }
1718 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719}
1720
1721 void
1722mch_print_set_font(int iBold, int iItalic, int iUnderline)
1723{
1724 SelectObject(prt_dlg.hDC, prt_font_handles[iBold][iItalic][iUnderline]);
1725}
1726
1727 void
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001728mch_print_set_bg(long_u bgcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729{
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001730 SetBkColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC,
1731 swap_me((COLORREF)bgcol)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732 /*
1733 * With a white background we can draw characters transparent, which is
1734 * good for italic characters that overlap to the next char cell.
1735 */
1736 if (bgcol == 0xffffffUL)
1737 SetBkMode(prt_dlg.hDC, TRANSPARENT);
1738 else
1739 SetBkMode(prt_dlg.hDC, OPAQUE);
1740}
1741
1742 void
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001743mch_print_set_fg(long_u fgcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001744{
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001745 SetTextColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC,
1746 swap_me((COLORREF)fgcol)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747}
1748
Bram Moolenaar0f873732019-12-05 20:28:46 +01001749#endif // FEAT_PRINTER && !FEAT_POSTSCRIPT
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750
Bram Moolenaar58d98232005-07-23 22:25:46 +00001751
1752
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753#if defined(FEAT_SHORTCUT) || defined(PROTO)
Bram Moolenaar82881492012-11-20 16:53:39 +01001754# ifndef PROTO
1755# include <shlobj.h>
1756# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001757
Bram Moolenaar4a792c82019-06-06 12:22:41 +02001758# define is_path_sep(c) ((c) == L'\\' || (c) == L'/')
1759
1760 static int
1761is_reparse_point_included(LPCWSTR fname)
1762{
1763 LPCWSTR p = fname, q;
1764 WCHAR buf[MAX_PATH];
1765 DWORD attr;
1766
Keith Thompson184f71c2024-01-04 21:19:04 +01001767 if (SAFE_isalpha(p[0]) && p[1] == L':' && is_path_sep(p[2]))
Bram Moolenaar4a792c82019-06-06 12:22:41 +02001768 p += 3;
1769 else if (is_path_sep(p[0]) && is_path_sep(p[1]))
1770 p += 2;
1771
1772 while (*p != L'\0')
1773 {
1774 q = wcspbrk(p, L"\\/");
1775 if (q == NULL)
1776 p = q = fname + wcslen(fname);
1777 else
1778 p = q + 1;
1779 if (q - fname >= MAX_PATH)
1780 return FALSE;
1781 wcsncpy(buf, fname, q - fname);
1782 buf[q - fname] = L'\0';
1783 attr = GetFileAttributesW(buf);
1784 if (attr != INVALID_FILE_ATTRIBUTES
1785 && (attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
1786 return TRUE;
1787 }
1788 return FALSE;
1789}
1790
AmberArrf5d0f542023-08-20 20:03:45 +02001791/*
1792 * Return the resolved file path, NULL if "fname" is an AppExecLink reparse
1793 * point, already fully resolved, or it doesn't exists.
1794 */
1795 char_u *
Bram Moolenaardce1e892019-02-10 23:18:53 +01001796resolve_reparse_point(char_u *fname)
1797{
1798 HANDLE h = INVALID_HANDLE_VALUE;
1799 DWORD size;
Bram Moolenaar3f9bdeb2019-08-01 13:55:37 +02001800 WCHAR *p, *wp;
Bram Moolenaardce1e892019-02-10 23:18:53 +01001801 char_u *rfname = NULL;
Bram Moolenaar3f9bdeb2019-08-01 13:55:37 +02001802 WCHAR *buff = NULL;
Bram Moolenaardce1e892019-02-10 23:18:53 +01001803
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001804 p = enc_to_utf16(fname, NULL);
1805 if (p == NULL)
1806 goto fail;
1807
Bram Moolenaar4a792c82019-06-06 12:22:41 +02001808 if (!is_reparse_point_included(p))
1809 {
1810 vim_free(p);
1811 goto fail;
1812 }
1813
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001814 h = CreateFileW(p, 0, 0, NULL, OPEN_EXISTING,
1815 FILE_FLAG_BACKUP_SEMANTICS, NULL);
1816 vim_free(p);
Bram Moolenaardce1e892019-02-10 23:18:53 +01001817
1818 if (h == INVALID_HANDLE_VALUE)
1819 goto fail;
1820
K.Takata27b53be2022-09-18 12:25:49 +01001821 size = GetFinalPathNameByHandleW(h, NULL, 0, 0);
Bram Moolenaar3f9bdeb2019-08-01 13:55:37 +02001822 if (size == 0)
1823 goto fail;
1824 buff = ALLOC_MULT(WCHAR, size);
1825 if (buff == NULL)
1826 goto fail;
K.Takata27b53be2022-09-18 12:25:49 +01001827 if (GetFinalPathNameByHandleW(h, buff, size, 0) == 0)
Bram Moolenaardce1e892019-02-10 23:18:53 +01001828 goto fail;
1829
Bram Moolenaar3f9bdeb2019-08-01 13:55:37 +02001830 if (wcsncmp(buff, L"\\\\?\\UNC\\", 8) == 0)
1831 {
1832 buff[6] = L'\\';
1833 wp = buff + 6;
1834 }
1835 else if (wcsncmp(buff, L"\\\\?\\", 4) == 0)
1836 wp = buff + 4;
Bram Moolenaardce1e892019-02-10 23:18:53 +01001837 else
Bram Moolenaar3f9bdeb2019-08-01 13:55:37 +02001838 wp = buff;
1839
1840 rfname = utf16_to_enc(wp, NULL);
Bram Moolenaardce1e892019-02-10 23:18:53 +01001841
1842fail:
1843 if (h != INVALID_HANDLE_VALUE)
1844 CloseHandle(h);
Bram Moolenaar3f9bdeb2019-08-01 13:55:37 +02001845 if (buff != NULL)
1846 vim_free(buff);
Bram Moolenaardce1e892019-02-10 23:18:53 +01001847
1848 return rfname;
1849}
1850
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851/*
1852 * When "fname" is the name of a shortcut (*.lnk) resolve the file it points
1853 * to and return that name in allocated memory.
1854 * Otherwise NULL is returned.
1855 */
Bram Moolenaardce1e892019-02-10 23:18:53 +01001856 static char_u *
1857resolve_shortcut(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001858{
1859 HRESULT hr;
1860 IShellLink *psl = NULL;
1861 IPersistFile *ppf = NULL;
1862 OLECHAR wsz[MAX_PATH];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 char_u *rfname = NULL;
1864 int len;
Bram Moolenaar604729e2013-08-30 16:44:19 +02001865 IShellLinkW *pslw = NULL;
1866 WIN32_FIND_DATAW ffdw; // we get those free of charge
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867
Bram Moolenaar0f873732019-12-05 20:28:46 +01001868 // Check if the file name ends in ".lnk". Avoid calling
1869 // CoCreateInstance(), it's quite slow.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870 if (fname == NULL)
1871 return rfname;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001872 len = (int)STRLEN(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001873 if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0)
1874 return rfname;
1875
1876 CoInitialize(NULL);
1877
1878 // create a link manager object and request its interface
1879 hr = CoCreateInstance(
1880 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001881 &IID_IShellLinkW, (void**)&pslw);
1882 if (hr == S_OK)
1883 {
1884 WCHAR *p = enc_to_utf16(fname, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001886 if (p != NULL)
1887 {
1888 // Get a pointer to the IPersistFile interface.
1889 hr = pslw->lpVtbl->QueryInterface(
1890 pslw, &IID_IPersistFile, (void**)&ppf);
1891 if (hr != S_OK)
1892 goto shortcut_errorw;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001893
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001894 // "load" the name and resolve the link
1895 hr = ppf->lpVtbl->Load(ppf, p, STGM_READ);
1896 if (hr != S_OK)
1897 goto shortcut_errorw;
1898# if 0 // This makes Vim wait a long time if the target does not exist.
1899 hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI);
1900 if (hr != S_OK)
1901 goto shortcut_errorw;
Bram Moolenaar604729e2013-08-30 16:44:19 +02001902# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001904 // Get the path to the link target.
1905 ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR));
1906 hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0);
1907 if (hr == S_OK && wsz[0] != NUL)
1908 rfname = utf16_to_enc(wsz, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001910shortcut_errorw:
1911 vim_free(p);
1912 }
1913 }
1914
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 // Release all interface pointers (both belong to the same object)
1916 if (ppf != NULL)
1917 ppf->lpVtbl->Release(ppf);
1918 if (psl != NULL)
1919 psl->lpVtbl->Release(psl);
Bram Moolenaar604729e2013-08-30 16:44:19 +02001920 if (pslw != NULL)
1921 pslw->lpVtbl->Release(pslw);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001922
1923 CoUninitialize();
1924 return rfname;
1925}
Bram Moolenaardce1e892019-02-10 23:18:53 +01001926
1927 char_u *
1928mch_resolve_path(char_u *fname, int reparse_point)
1929{
1930 char_u *path = resolve_shortcut(fname);
1931
1932 if (path == NULL && reparse_point)
1933 path = resolve_reparse_point(fname);
1934 return path;
1935}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936#endif
1937
Bram Moolenaarafde13b2019-04-28 19:46:49 +02001938#if (defined(FEAT_EVAL) && (!defined(FEAT_GUI) || defined(VIMDLL))) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939/*
1940 * Bring ourselves to the foreground. Does work if the OS doesn't allow it.
1941 */
1942 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001943win32_set_foreground(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001944{
Bram Moolenaar0f873732019-12-05 20:28:46 +01001945 GetConsoleHwnd(); // get value of s_hwnd
Bram Moolenaar071d4272004-06-13 20:20:40 +00001946 if (s_hwnd != 0)
1947 SetForegroundWindow(s_hwnd);
1948}
1949#endif
1950
1951#if defined(FEAT_CLIENTSERVER) || defined(PROTO)
1952/*
1953 * Client-server code for Vim
1954 *
1955 * Originally written by Paul Moore
1956 */
1957
Bram Moolenaar0f873732019-12-05 20:28:46 +01001958// In order to handle inter-process messages, we need to have a window. But
1959// the functions in this module can be called before the main GUI window is
1960// created (and may also be called in the console version, where there is no
1961// GUI window at all).
1962//
1963// So we create a hidden window, and arrange to destroy it on exit.
1964HWND message_window = 0; // window that's handling messages
Bram Moolenaar071d4272004-06-13 20:20:40 +00001965
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001966# define VIM_CLASSNAME "VIM_MESSAGES"
1967# define VIM_CLASSNAME_LEN (sizeof(VIM_CLASSNAME) - 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001968
K.Takataf9f2a332022-06-17 20:05:40 +01001969// Timeout for sending a message to another Vim instance. Normally this works
1970// instantly, but it may hang when the other Vim instance is halted.
1971# define SENDMESSAGE_TIMEOUT (5 * 1000)
1972
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00001973// Communication is via WM_COPYDATA messages. The message type is sent in
Bram Moolenaar0f873732019-12-05 20:28:46 +01001974// the dwData parameter. Types are defined here.
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01001975# define COPYDATA_KEYS 0
1976# define COPYDATA_REPLY 1
1977# define COPYDATA_EXPR 10
1978# define COPYDATA_RESULT 11
1979# define COPYDATA_ERROR_RESULT 12
1980# define COPYDATA_ENCODING 20
Bram Moolenaar071d4272004-06-13 20:20:40 +00001981
Bram Moolenaar0f873732019-12-05 20:28:46 +01001982// This is a structure containing a server HWND and its name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001983struct server_id
1984{
1985 HWND hwnd;
1986 char_u *name;
1987};
1988
Bram Moolenaar0f873732019-12-05 20:28:46 +01001989// Last received 'encoding' that the client uses.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001990static char_u *client_enc = NULL;
1991
1992/*
1993 * Tell the other side what encoding we are using.
K.Takataf9f2a332022-06-17 20:05:40 +01001994 * Return -1 if timeout happens. Other errors are ignored.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001995 */
K.Takataf9f2a332022-06-17 20:05:40 +01001996 static int
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001997serverSendEnc(HWND target)
1998{
1999 COPYDATASTRUCT data;
2000
2001 data.dwData = COPYDATA_ENCODING;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002002 data.cbData = (DWORD)STRLEN(p_enc) + 1;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002003 data.lpData = p_enc;
K.Takataf9f2a332022-06-17 20:05:40 +01002004 if (SendMessageTimeout(target, WM_COPYDATA,
2005 (WPARAM)message_window, (LPARAM)&data,
2006 SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, NULL) == 0)
2007 return -1;
2008 return 0;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002009}
2010
Bram Moolenaar071d4272004-06-13 20:20:40 +00002011/*
2012 * Clean up on exit. This destroys the hidden message window.
2013 */
2014 static void
Bram Moolenaar071d4272004-06-13 20:20:40 +00002015CleanUpMessaging(void)
2016{
Yegappan Lakshmanana41e2212023-01-16 18:19:05 +00002017 if (message_window == 0)
2018 return;
2019
2020 DestroyWindow(message_window);
2021 message_window = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022}
2023
2024static int save_reply(HWND server, char_u *reply, int expr);
2025
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02002026/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 * The window procedure for the hidden message window.
2028 * It handles callback messages and notifications from servers.
2029 * In order to process these messages, it is necessary to run a
2030 * message loop. Code which may run before the main message loop
2031 * is started (in the GUI) is careful to pump messages when it needs
2032 * to. Features which require message delivery during normal use will
2033 * not work in the console version - this basically means those
2034 * features which allow Vim to act as a server, rather than a client.
2035 */
2036 static LRESULT CALLBACK
2037Messaging_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2038{
2039 if (msg == WM_COPYDATA)
2040 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002041 // This is a message from another Vim. The dwData member of the
2042 // COPYDATASTRUCT determines the type of message:
2043 // COPYDATA_ENCODING:
2044 // The encoding that the client uses. Following messages will
2045 // use this encoding, convert if needed.
2046 // COPYDATA_KEYS:
2047 // A key sequence. We are a server, and a client wants these keys
2048 // adding to the input queue.
2049 // COPYDATA_REPLY:
2050 // A reply. We are a client, and a server has sent this message
2051 // in response to a request. (server2client())
2052 // COPYDATA_EXPR:
2053 // An expression. We are a server, and a client wants us to
2054 // evaluate this expression.
2055 // COPYDATA_RESULT:
2056 // A reply. We are a client, and a server has sent this message
2057 // in response to a COPYDATA_EXPR.
2058 // COPYDATA_ERROR_RESULT:
2059 // A reply. We are a client, and a server has sent this message
2060 // in response to a COPYDATA_EXPR that failed to evaluate.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061 COPYDATASTRUCT *data = (COPYDATASTRUCT*)lParam;
2062 HWND sender = (HWND)wParam;
2063 COPYDATASTRUCT reply;
2064 char_u *res;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002065 int retval;
K.Takataf9f2a332022-06-17 20:05:40 +01002066 DWORD_PTR dwret = 0;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002067 char_u *str;
2068 char_u *tofree;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069
2070 switch (data->dwData)
2071 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002072 case COPYDATA_ENCODING:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002073 // Remember the encoding that the client uses.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002074 vim_free(client_enc);
2075 client_enc = enc_canonize((char_u *)data->lpData);
2076 return 1;
2077
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 case COPYDATA_KEYS:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002079 // Remember who sent this, for <client>
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080 clientWindow = sender;
2081
Bram Moolenaar0f873732019-12-05 20:28:46 +01002082 // Add the received keys to the input buffer. The loop waiting
2083 // for the user to do something should check the input buffer.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002084 str = serverConvert(client_enc, (char_u *)data->lpData, &tofree);
2085 server_to_input_buf(str);
2086 vim_free(tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087
2088# ifdef FEAT_GUI
Bram Moolenaar0f873732019-12-05 20:28:46 +01002089 // Wake up the main GUI loop.
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002090# ifdef VIMDLL
2091 if (gui.in_use)
2092# endif
2093 if (s_hwnd != 0)
2094 PostMessage(s_hwnd, WM_NULL, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002095# endif
2096 return 1;
2097
2098 case COPYDATA_EXPR:
Bram Moolenaar0f873732019-12-05 20:28:46 +01002099 // Remember who sent this, for <client>
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100 clientWindow = sender;
2101
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002102 str = serverConvert(client_enc, (char_u *)data->lpData, &tofree);
2103 res = eval_client_expr_to_string(str);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002104
Bram Moolenaar071d4272004-06-13 20:20:40 +00002105 if (res == NULL)
2106 {
Bram Moolenaar74409f62022-01-01 15:58:22 +00002107 char *err = _(e_invalid_expression_received);
Bram Moolenaar15bf76d2017-03-18 16:18:37 +01002108 size_t len = STRLEN(str) + STRLEN(err) + 5;
2109
Bram Moolenaar964b3742019-05-24 18:54:09 +02002110 res = alloc(len);
Bram Moolenaar15bf76d2017-03-18 16:18:37 +01002111 if (res != NULL)
John Marriott51f6a782025-05-04 21:35:36 +02002112 reply.cbData = (DWORD)vim_snprintf_safelen(
2113 (char *)res, len, "%s: \"%s\"", err, str);
2114 else
2115 reply.cbData = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116 reply.dwData = COPYDATA_ERROR_RESULT;
2117 }
2118 else
John Marriott51f6a782025-05-04 21:35:36 +02002119 {
2120 reply.cbData = (DWORD)STRLEN(res) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121 reply.dwData = COPYDATA_RESULT;
John Marriott51f6a782025-05-04 21:35:36 +02002122 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002123 reply.lpData = res;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002124
K.Takataf9f2a332022-06-17 20:05:40 +01002125 if (serverSendEnc(sender) < 0)
2126 retval = -1;
2127 else
2128 {
2129 if (SendMessageTimeout(sender, WM_COPYDATA,
2130 (WPARAM)message_window, (LPARAM)&reply,
2131 SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, &dwret) == 0)
2132 retval = -1;
2133 else
2134 retval = (int)dwret;
2135 }
Bram Moolenaar15bf76d2017-03-18 16:18:37 +01002136 vim_free(tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002137 vim_free(res);
2138 return retval;
2139
2140 case COPYDATA_REPLY:
2141 case COPYDATA_RESULT:
2142 case COPYDATA_ERROR_RESULT:
2143 if (data->lpData != NULL)
2144 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002145 str = serverConvert(client_enc, (char_u *)data->lpData,
2146 &tofree);
2147 if (tofree == NULL)
2148 str = vim_strsave(str);
2149 if (save_reply(sender, str,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002150 (data->dwData == COPYDATA_REPLY ? 0 :
2151 (data->dwData == COPYDATA_RESULT ? 1 :
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002152 2))) == FAIL)
2153 vim_free(str);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002154 else if (data->dwData == COPYDATA_REPLY)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155 {
Bram Moolenaar427d51c2013-06-16 16:01:25 +02002156 char_u winstr[30];
2157
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002158 sprintf((char *)winstr, PRINTF_HEX_LONG_U, (long_u)sender);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002159 apply_autocmds(EVENT_REMOTEREPLY, winstr, str,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002160 TRUE, curbuf);
2161 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002162 }
2163 return 1;
2164 }
2165
2166 return 0;
2167 }
2168
2169 else if (msg == WM_ACTIVATE && wParam == WA_ACTIVE)
2170 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002171 // When the message window is activated (brought to the foreground),
2172 // this actually applies to the text window.
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002173# if !defined(FEAT_GUI) || defined(VIMDLL)
2174# ifdef VIMDLL
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002175 if (!gui.in_use)
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002176# endif
Bram Moolenaar0f873732019-12-05 20:28:46 +01002177 GetConsoleHwnd(); // get value of s_hwnd
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002178# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002179 if (s_hwnd != 0)
2180 {
2181 SetForegroundWindow(s_hwnd);
2182 return 0;
2183 }
2184 }
2185
2186 return DefWindowProc(hwnd, msg, wParam, lParam);
2187}
2188
2189/*
2190 * Initialise the message handling process. This involves creating a window
2191 * to handle messages - the window will not be visible.
2192 */
2193 void
2194serverInitMessaging(void)
2195{
2196 WNDCLASS wndclass;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197
Bram Moolenaar0f873732019-12-05 20:28:46 +01002198 // Clean up on exit
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 atexit(CleanUpMessaging);
2200
Bram Moolenaar0f873732019-12-05 20:28:46 +01002201 // Register a window class - we only really care
2202 // about the window procedure
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 wndclass.style = 0;
2204 wndclass.lpfnWndProc = Messaging_WndProc;
2205 wndclass.cbClsExtra = 0;
2206 wndclass.cbWndExtra = 0;
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002207 wndclass.hInstance = g_hinst;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 wndclass.hIcon = NULL;
2209 wndclass.hCursor = NULL;
2210 wndclass.hbrBackground = NULL;
2211 wndclass.lpszMenuName = NULL;
2212 wndclass.lpszClassName = VIM_CLASSNAME;
2213 RegisterClass(&wndclass);
2214
Bram Moolenaar0f873732019-12-05 20:28:46 +01002215 // Create the message window. It will be hidden, so the details don't
2216 // matter. Don't use WS_OVERLAPPEDWINDOW, it will make a shortcut remove
2217 // focus from gvim.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002218 message_window = CreateWindow(VIM_CLASSNAME, "",
2219 WS_POPUPWINDOW | WS_CAPTION,
2220 CW_USEDEFAULT, CW_USEDEFAULT,
2221 100, 100, NULL, NULL,
Bram Moolenaarafde13b2019-04-28 19:46:49 +02002222 g_hinst, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223}
2224
Bram Moolenaar0f873732019-12-05 20:28:46 +01002225// Used by serverSendToVim() to find an alternate server name.
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002226static char_u *altname_buf_ptr = NULL;
2227
Bram Moolenaar071d4272004-06-13 20:20:40 +00002228/*
2229 * Get the title of the window "hwnd", which is the Vim server name, in
2230 * "name[namelen]" and return the length.
2231 * Returns zero if window "hwnd" is not a Vim server.
2232 */
2233 static int
2234getVimServerName(HWND hwnd, char *name, int namelen)
2235{
2236 int len;
2237 char buffer[VIM_CLASSNAME_LEN + 1];
2238
Bram Moolenaar0f873732019-12-05 20:28:46 +01002239 // Ignore windows which aren't Vim message windows
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240 len = GetClassName(hwnd, buffer, sizeof(buffer));
2241 if (len != VIM_CLASSNAME_LEN || STRCMP(buffer, VIM_CLASSNAME) != 0)
2242 return 0;
2243
Bram Moolenaar0f873732019-12-05 20:28:46 +01002244 // Get the title of the window
Bram Moolenaar071d4272004-06-13 20:20:40 +00002245 return GetWindowText(hwnd, name, namelen);
2246}
2247
2248 static BOOL CALLBACK
2249enumWindowsGetServer(HWND hwnd, LPARAM lparam)
2250{
2251 struct server_id *id = (struct server_id *)lparam;
2252 char server[MAX_PATH];
2253
Bram Moolenaar0f873732019-12-05 20:28:46 +01002254 // Get the title of the window
Bram Moolenaar071d4272004-06-13 20:20:40 +00002255 if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2256 return TRUE;
2257
Bram Moolenaar0f873732019-12-05 20:28:46 +01002258 // If this is the server we're looking for, return its HWND
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 if (STRICMP(server, id->name) == 0)
2260 {
2261 id->hwnd = hwnd;
2262 return FALSE;
2263 }
2264
Bram Moolenaar0f873732019-12-05 20:28:46 +01002265 // If we are looking for an alternate server, remember this name.
John Marriott51f6a782025-05-04 21:35:36 +02002266 if (altname_buf_ptr != NULL)
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002267 {
John Marriott51f6a782025-05-04 21:35:36 +02002268 size_t namelen = STRLEN(id->name);
2269
2270 if (STRNICMP(server, id->name, namelen) == 0
2271 && vim_isdigit(server[namelen]))
2272 {
2273 STRCPY(altname_buf_ptr, server);
2274 altname_buf_ptr = NULL; // don't use another name
2275 }
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002276 }
2277
Bram Moolenaar0f873732019-12-05 20:28:46 +01002278 // Otherwise, keep looking
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279 return TRUE;
2280}
2281
2282 static BOOL CALLBACK
2283enumWindowsGetNames(HWND hwnd, LPARAM lparam)
2284{
2285 garray_T *ga = (garray_T *)lparam;
2286 char server[MAX_PATH];
2287
Bram Moolenaar0f873732019-12-05 20:28:46 +01002288 // Get the title of the window
Bram Moolenaar071d4272004-06-13 20:20:40 +00002289 if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2290 return TRUE;
2291
Bram Moolenaar0f873732019-12-05 20:28:46 +01002292 // Add the name to the list
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002293 ga_concat(ga, (char_u *)server);
2294 ga_concat(ga, (char_u *)"\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002295 return TRUE;
2296}
2297
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002298struct enum_windows_s
2299{
2300 WNDENUMPROC lpEnumFunc;
2301 LPARAM lParam;
2302};
2303
2304 static BOOL CALLBACK
2305enum_windows_child(HWND hwnd, LPARAM lParam)
2306{
2307 struct enum_windows_s *ew = (struct enum_windows_s *)lParam;
2308
2309 return (ew->lpEnumFunc)(hwnd, ew->lParam);
2310}
2311
2312 static BOOL CALLBACK
2313enum_windows_toplevel(HWND hwnd, LPARAM lParam)
2314{
2315 struct enum_windows_s *ew = (struct enum_windows_s *)lParam;
2316
Bram Moolenaar95ba5c32018-10-07 22:47:07 +02002317 if ((ew->lpEnumFunc)(hwnd, ew->lParam))
2318 return TRUE;
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002319 return EnumChildWindows(hwnd, enum_windows_child, lParam);
2320}
2321
Bram Moolenaar0f873732019-12-05 20:28:46 +01002322/*
2323 * Enumerate all windows including children.
2324 */
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002325 static BOOL
2326enum_windows(WNDENUMPROC lpEnumFunc, LPARAM lParam)
2327{
2328 struct enum_windows_s ew;
2329
2330 ew.lpEnumFunc = lpEnumFunc;
2331 ew.lParam = lParam;
2332 return EnumWindows(enum_windows_toplevel, (LPARAM)&ew);
2333}
2334
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335 static HWND
2336findServer(char_u *name)
2337{
2338 struct server_id id;
2339
2340 id.name = name;
2341 id.hwnd = 0;
2342
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002343 enum_windows(enumWindowsGetServer, (LPARAM)(&id));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002344
2345 return id.hwnd;
2346}
2347
2348 void
2349serverSetName(char_u *name)
2350{
John Marriott7ddba512025-04-17 20:35:42 +02002351 size_t namelen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 char_u *ok_name;
2353 HWND hwnd = 0;
2354 int i = 0;
2355 char_u *p;
2356
Bram Moolenaar0f873732019-12-05 20:28:46 +01002357 // Leave enough space for a 9-digit suffix to ensure uniqueness!
John Marriott7ddba512025-04-17 20:35:42 +02002358 namelen = STRLEN(name);
2359 ok_name = alloc(namelen + 10);
2360 if (ok_name == NULL)
2361 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002362
2363 STRCPY(ok_name, name);
John Marriott7ddba512025-04-17 20:35:42 +02002364 p = ok_name + namelen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365
2366 for (;;)
2367 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002368 // This is inefficient - we're doing an EnumWindows loop for each
2369 // possible name. It would be better to grab all names in one go,
2370 // and scan the list each time...
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371 hwnd = findServer(ok_name);
2372 if (hwnd == 0)
2373 break;
2374
2375 ++i;
2376 if (i >= 1000)
2377 break;
2378
2379 sprintf((char *)p, "%d", i);
2380 }
2381
2382 if (hwnd != 0)
2383 vim_free(ok_name);
2384 else
2385 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002386 // Remember the name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387 serverName = ok_name;
Bram Moolenaar0f873732019-12-05 20:28:46 +01002388 need_maketitle = TRUE; // update Vim window title later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389
Bram Moolenaar0f873732019-12-05 20:28:46 +01002390 // Update the message window title
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002391 SetWindowText(message_window, (LPCSTR)ok_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002392
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002393# ifdef FEAT_EVAL
Bram Moolenaar0f873732019-12-05 20:28:46 +01002394 // Set the servername variable
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395 set_vim_var_string(VV_SEND_SERVER, serverName, -1);
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002396# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002397 }
2398}
2399
2400 char_u *
2401serverGetVimNames(void)
2402{
2403 garray_T ga;
2404
2405 ga_init2(&ga, 1, 100);
2406
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002407 enum_windows(enumWindowsGetNames, (LPARAM)(&ga));
Bram Moolenaar269ec652004-07-29 08:43:53 +00002408 ga_append(&ga, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409
2410 return ga.ga_data;
2411}
2412
2413 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002414serverSendReply(
Bram Moolenaar0f873732019-12-05 20:28:46 +01002415 char_u *name, // Where to send.
2416 char_u *reply) // What to send.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002417{
2418 HWND target;
2419 COPYDATASTRUCT data;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002420 long_u n = 0;
K.Takataf9f2a332022-06-17 20:05:40 +01002421 DWORD_PTR dwret = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422
Bram Moolenaar0f873732019-12-05 20:28:46 +01002423 // The "name" argument is a magic cookie obtained from expand("<client>").
2424 // It should be of the form 0xXXXXX - i.e. a C hex literal, which is the
2425 // value of the client's message window HWND.
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002426 sscanf((char *)name, SCANF_HEX_LONG_U, &n);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 if (n == 0)
2428 return -1;
2429
2430 target = (HWND)n;
2431 if (!IsWindow(target))
2432 return -1;
2433
2434 data.dwData = COPYDATA_REPLY;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002435 data.cbData = (DWORD)STRLEN(reply) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002436 data.lpData = reply;
2437
K.Takataf9f2a332022-06-17 20:05:40 +01002438 if (serverSendEnc(target) < 0)
2439 return -1;
2440 if (SendMessageTimeout(target, WM_COPYDATA,
2441 (WPARAM)message_window, (LPARAM)&data,
2442 SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, &dwret) == 0)
2443 return -1;
2444 return dwret ? 0 : -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002445}
2446
2447 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002448serverSendToVim(
Bram Moolenaar0f873732019-12-05 20:28:46 +01002449 char_u *name, // Where to send.
2450 char_u *cmd, // What to send.
2451 char_u **result, // Result of eval'ed expression
2452 void *ptarget, // HWND of server
2453 int asExpr, // Expression or keys?
2454 int timeout, // timeout in seconds or zero
2455 int silent) // don't complain about no server
Bram Moolenaar071d4272004-06-13 20:20:40 +00002456{
John Marriott51f6a782025-05-04 21:35:36 +02002457 size_t namelen;
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002458 HWND target;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002459 COPYDATASTRUCT data;
2460 char_u *retval = NULL;
2461 int retcode = 0;
K.Takataf9f2a332022-06-17 20:05:40 +01002462 DWORD_PTR dwret = 0;
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002463 char_u altname_buf[MAX_PATH];
2464
Bram Moolenaar0f873732019-12-05 20:28:46 +01002465 // Execute locally if no display or target is ourselves
Bram Moolenaar7416f3e2017-03-18 18:10:13 +01002466 if (serverName != NULL && STRICMP(name, serverName) == 0)
2467 return sendToLocalVim(cmd, asExpr, result);
2468
Bram Moolenaar0f873732019-12-05 20:28:46 +01002469 // If the server name does not end in a digit then we look for an
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00002470 // alternate name. e.g. when "name" is GVIM then we may find GVIM2.
John Marriott51f6a782025-05-04 21:35:36 +02002471 namelen = STRLEN(name);
2472 if (namelen > 1 && !vim_isdigit(name[namelen - 1]))
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002473 altname_buf_ptr = altname_buf;
2474 altname_buf[0] = NUL;
2475 target = findServer(name);
2476 altname_buf_ptr = NULL;
2477 if (target == 0 && altname_buf[0] != NUL)
Bram Moolenaar0f873732019-12-05 20:28:46 +01002478 // Use another server name we found.
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002479 target = findServer(altname_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002480
2481 if (target == 0)
2482 {
2483 if (!silent)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002484 semsg(_(e_no_registered_server_named_str), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002485 return -1;
2486 }
2487
2488 if (ptarget)
2489 *(HWND *)ptarget = target;
2490
2491 data.dwData = asExpr ? COPYDATA_EXPR : COPYDATA_KEYS;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002492 data.cbData = (DWORD)STRLEN(cmd) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493 data.lpData = cmd;
2494
K.Takataf9f2a332022-06-17 20:05:40 +01002495 if (serverSendEnc(target) < 0)
2496 return -1;
2497 if (SendMessageTimeout(target, WM_COPYDATA,
2498 (WPARAM)message_window, (LPARAM)&data,
2499 SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, &dwret) == 0)
2500 return -1;
2501 if (dwret == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002502 return -1;
2503
2504 if (asExpr)
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002505 retval = serverGetReply(target, &retcode, TRUE, TRUE, timeout);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506
2507 if (result == NULL)
2508 vim_free(retval);
2509 else
Bram Moolenaar0f873732019-12-05 20:28:46 +01002510 *result = retval; // Caller assumes responsibility for freeing
Bram Moolenaar071d4272004-06-13 20:20:40 +00002511
2512 return retcode;
2513}
2514
2515/*
2516 * Bring the server to the foreground.
2517 */
2518 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002519serverForeground(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002520{
2521 HWND target = findServer(name);
2522
2523 if (target != 0)
2524 SetForegroundWindow(target);
2525}
2526
Bram Moolenaar0f873732019-12-05 20:28:46 +01002527// Replies from server need to be stored until the client picks them up via
2528// remote_read(). So we maintain a list of server-id/reply pairs.
2529// Note that there could be multiple replies from one server pending if the
2530// client is slow picking them up.
2531// We just store the replies in a simple list. When we remove an entry, we
2532// move list entries down to fill the gap.
2533// The server ID is simply the HWND.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534typedef struct
2535{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002536 HWND server; // server window
2537 char_u *reply; // reply string
2538 int expr_result; // 0 for REPLY, 1 for RESULT 2 for error
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002539} reply_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002540
2541static garray_T reply_list = {0, 0, sizeof(reply_T), 5, 0};
2542
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002543# define REPLY_ITEM(i) ((reply_T *)(reply_list.ga_data) + (i))
2544# define REPLY_COUNT (reply_list.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545
Bram Moolenaar0f873732019-12-05 20:28:46 +01002546// Flag which is used to wait for a reply
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547static int reply_received = 0;
2548
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002549/*
2550 * Store a reply. "reply" must be allocated memory (or NULL).
2551 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552 static int
2553save_reply(HWND server, char_u *reply, int expr)
2554{
2555 reply_T *rep;
2556
2557 if (ga_grow(&reply_list, 1) == FAIL)
2558 return FAIL;
2559
2560 rep = REPLY_ITEM(REPLY_COUNT);
2561 rep->server = server;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002562 rep->reply = reply;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002563 rep->expr_result = expr;
2564 if (rep->reply == NULL)
2565 return FAIL;
2566
2567 ++REPLY_COUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002568 reply_received = 1;
2569 return OK;
2570}
2571
2572/*
2573 * Get a reply from server "server".
2574 * When "expr_res" is non NULL, get the result of an expression, otherwise a
2575 * server2client() message.
2576 * When non NULL, point to return code. 0 => OK, -1 => ERROR
2577 * If "remove" is TRUE, consume the message, the caller must free it then.
2578 * if "wait" is TRUE block until a message arrives (or the server exits).
2579 */
2580 char_u *
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002581serverGetReply(HWND server, int *expr_res, int remove, int wait, int timeout)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582{
2583 int i;
2584 char_u *reply;
2585 reply_T *rep;
Bram Moolenaar15e737f2017-03-18 21:22:47 +01002586 int did_process = FALSE;
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002587 time_t start;
2588 time_t now;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002589
Bram Moolenaar0f873732019-12-05 20:28:46 +01002590 // When waiting, loop until the message waiting for is received.
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002591 time(&start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592 for (;;)
2593 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002594 // Reset this here, in case a message arrives while we are going
2595 // through the already received messages.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596 reply_received = 0;
2597
2598 for (i = 0; i < REPLY_COUNT; ++i)
2599 {
2600 rep = REPLY_ITEM(i);
2601 if (rep->server == server
2602 && ((rep->expr_result != 0) == (expr_res != NULL)))
2603 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002604 // Save the values we've found for later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605 reply = rep->reply;
2606 if (expr_res != NULL)
2607 *expr_res = rep->expr_result == 1 ? 0 : -1;
2608
2609 if (remove)
2610 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002611 // Move the rest of the list down to fill the gap
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612 mch_memmove(rep, rep + 1,
2613 (REPLY_COUNT - i - 1) * sizeof(reply_T));
2614 --REPLY_COUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002615 }
2616
Bram Moolenaar0f873732019-12-05 20:28:46 +01002617 // Return the reply to the caller, who takes on responsibility
2618 // for freeing it if "remove" is TRUE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002619 return reply;
2620 }
2621 }
2622
Bram Moolenaar0f873732019-12-05 20:28:46 +01002623 // If we got here, we didn't find a reply. Return immediately if the
2624 // "wait" parameter isn't set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002625 if (!wait)
Bram Moolenaar15e737f2017-03-18 21:22:47 +01002626 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002627 // Process pending messages once. Without this, looping on
2628 // remote_peek() would never get the reply.
Bram Moolenaar15e737f2017-03-18 21:22:47 +01002629 if (!did_process)
2630 {
2631 did_process = TRUE;
2632 serverProcessPendingMessages();
2633 continue;
2634 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635 break;
Bram Moolenaar15e737f2017-03-18 21:22:47 +01002636 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002637
Bram Moolenaar0f873732019-12-05 20:28:46 +01002638 // We need to wait for a reply. Enter a message loop until the
2639 // "reply_received" flag gets set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640
Bram Moolenaar0f873732019-12-05 20:28:46 +01002641 // Loop until we receive a reply
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 while (reply_received == 0)
2643 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002644# ifdef FEAT_TIMERS
Bram Moolenaar0f873732019-12-05 20:28:46 +01002645 // TODO: use the return value to decide how long to wait.
Bram Moolenaar42205552017-03-18 19:42:22 +01002646 check_due_timer();
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002647# endif
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002648 time(&now);
2649 if (timeout > 0 && (now - start) >= timeout)
2650 break;
2651
Bram Moolenaar0f873732019-12-05 20:28:46 +01002652 // Wait for a SendMessage() call to us. This could be the reply
2653 // we are waiting for. Use a timeout of a second, to catch the
2654 // situation that the server died unexpectedly.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655 MsgWaitForMultipleObjects(0, NULL, TRUE, 1000, QS_ALLINPUT);
2656
Bram Moolenaar0f873732019-12-05 20:28:46 +01002657 // If the server has died, give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00002658 if (!IsWindow(server))
2659 return NULL;
2660
2661 serverProcessPendingMessages();
2662 }
2663 }
2664
2665 return NULL;
2666}
2667
2668/*
2669 * Process any messages in the Windows message queue.
2670 */
2671 void
2672serverProcessPendingMessages(void)
2673{
2674 MSG msg;
2675
K.Takatab7057bd2022-01-21 11:37:07 +00002676 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 {
2678 TranslateMessage(&msg);
K.Takatab7057bd2022-01-21 11:37:07 +00002679 DispatchMessageW(&msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 }
2681}
2682
Bram Moolenaar0f873732019-12-05 20:28:46 +01002683#endif // FEAT_CLIENTSERVER
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684
2685#if defined(FEAT_GUI) || (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) \
2686 || defined(PROTO)
2687
2688struct charset_pair
2689{
John Marriott51f6a782025-05-04 21:35:36 +02002690 string_T name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 BYTE charset;
2692};
2693
John Marriott51f6a782025-05-04 21:35:36 +02002694#define STRING_INIT(s) \
2695 {(char_u *)(s), STRLEN_LITERAL(s)}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002696static struct charset_pair
2697charset_pairs[] =
2698{
John Marriott51f6a782025-05-04 21:35:36 +02002699 {STRING_INIT("ANSI"), ANSI_CHARSET},
2700 {STRING_INIT("CHINESEBIG5"), CHINESEBIG5_CHARSET},
2701 {STRING_INIT("DEFAULT"), DEFAULT_CHARSET},
2702 {STRING_INIT("HANGEUL"), HANGEUL_CHARSET},
2703 {STRING_INIT("OEM"), OEM_CHARSET},
2704 {STRING_INIT("SHIFTJIS"), SHIFTJIS_CHARSET},
2705 {STRING_INIT("SYMBOL"), SYMBOL_CHARSET},
2706 {STRING_INIT("ARABIC"), ARABIC_CHARSET},
2707 {STRING_INIT("BALTIC"), BALTIC_CHARSET},
2708 {STRING_INIT("EASTEUROPE"), EASTEUROPE_CHARSET},
2709 {STRING_INIT("GB2312"), GB2312_CHARSET},
2710 {STRING_INIT("GREEK"), GREEK_CHARSET},
2711 {STRING_INIT("HEBREW"), HEBREW_CHARSET},
2712 {STRING_INIT("JOHAB"), JOHAB_CHARSET},
2713 {STRING_INIT("MAC"), MAC_CHARSET},
2714 {STRING_INIT("RUSSIAN"), RUSSIAN_CHARSET},
2715 {STRING_INIT("THAI"), THAI_CHARSET},
2716 {STRING_INIT("TURKISH"), TURKISH_CHARSET}
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002717# ifdef VIETNAMESE_CHARSET
John Marriott51f6a782025-05-04 21:35:36 +02002718 ,
2719 {STRING_INIT("VIETNAMESE"), VIETNAMESE_CHARSET}
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002720# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002721};
2722
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002723struct quality_pair
2724{
John Marriott51f6a782025-05-04 21:35:36 +02002725 string_T name;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002726 DWORD quality;
2727};
2728
2729static struct quality_pair
2730quality_pairs[] = {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002731# ifdef CLEARTYPE_QUALITY
John Marriott51f6a782025-05-04 21:35:36 +02002732 {STRING_INIT("CLEARTYPE"), CLEARTYPE_QUALITY},
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002733# endif
2734# ifdef ANTIALIASED_QUALITY
John Marriott51f6a782025-05-04 21:35:36 +02002735 {STRING_INIT("ANTIALIASED"), ANTIALIASED_QUALITY},
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002736# endif
2737# ifdef NONANTIALIASED_QUALITY
John Marriott51f6a782025-05-04 21:35:36 +02002738 {STRING_INIT("NONANTIALIASED"), NONANTIALIASED_QUALITY},
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002739# endif
2740# ifdef PROOF_QUALITY
John Marriott51f6a782025-05-04 21:35:36 +02002741 {STRING_INIT("PROOF"), PROOF_QUALITY},
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002742# endif
2743# ifdef DRAFT_QUALITY
John Marriott51f6a782025-05-04 21:35:36 +02002744 {STRING_INIT("DRAFT"), DRAFT_QUALITY},
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002745# endif
John Marriott51f6a782025-05-04 21:35:36 +02002746 {STRING_INIT("DEFAULT"), DEFAULT_QUALITY}
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002747};
John Marriott51f6a782025-05-04 21:35:36 +02002748#undef STRING_INIT
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002749
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750/*
2751 * Convert a charset ID to a name.
2752 * Return NULL when not recognized.
2753 */
2754 char *
2755charset_id2name(int id)
2756{
John Marriott51f6a782025-05-04 21:35:36 +02002757 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002758
John Marriott51f6a782025-05-04 21:35:36 +02002759 for (i = 0; i < (int)ARRAY_LENGTH(charset_pairs); ++i)
2760 {
2761 if ((BYTE)id == charset_pairs[i].charset)
2762 return (char *)charset_pairs[i].name.string;
2763 }
2764
2765 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002766}
2767
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002768/*
2769 * Convert a quality ID to a name.
2770 * Return NULL when not recognized.
2771 */
2772 char *
2773quality_id2name(DWORD id)
2774{
John Marriott51f6a782025-05-04 21:35:36 +02002775 int i;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002776
John Marriott51f6a782025-05-04 21:35:36 +02002777 for (i = 0; i < (int)ARRAY_LENGTH(quality_pairs); ++i)
2778 {
2779 if (id == quality_pairs[i].quality)
2780 return (char *)quality_pairs[i].name.string;
2781 }
2782
2783 return NULL;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002784}
2785
Ken Takatad8cb1dd2024-01-12 18:09:43 +01002786// The default font height in 100% scaling (96dpi).
matveytad3b6a32024-12-08 10:26:51 +01002787// (-16 in 96dpi equates to roughly 12pt)
2788#define DEFAULT_FONT_HEIGHT (-16)
Ken Takatad8cb1dd2024-01-12 18:09:43 +01002789
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002790static const LOGFONTW s_lfDefault =
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791{
Ken Takatad8cb1dd2024-01-12 18:09:43 +01002792 DEFAULT_FONT_HEIGHT,
2793 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002794 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2795 PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE,
Ken Takatad8cb1dd2024-01-12 18:09:43 +01002796 L"" // Default font name will be set later based on current language.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797};
2798
Ken Takatad8cb1dd2024-01-12 18:09:43 +01002799// This will be initialized when set_default_logfont() is called first time.
2800// The value will be based on the system DPI.
2801int current_font_height = 0; // also used in gui_w32.c
Bram Moolenaar071d4272004-06-13 20:20:40 +00002802
Bram Moolenaar0f873732019-12-05 20:28:46 +01002803/*
2804 * Convert a string representing a point size into pixels. The string should
Bram Moolenaar071d4272004-06-13 20:20:40 +00002805 * be a positive decimal number, with an optional decimal point (eg, "12", or
2806 * "10.5"). The pixel value is returned, and a pointer to the next unconverted
2807 * character is stored in *end. The flag "vertical" says whether this
2808 * calculation is for a vertical (height) size or a horizontal (width) one.
2809 */
2810 static int
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002811points_to_pixels(WCHAR *str, WCHAR **end, int vertical, long_i pprinter_dc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002812{
2813 int pixels;
2814 int points = 0;
2815 int divisor = 0;
2816 HWND hwnd = (HWND)0;
2817 HDC hdc;
2818 HDC printer_dc = (HDC)pprinter_dc;
2819
2820 while (*str != NUL)
2821 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002822 if (*str == L'.' && divisor == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823 {
Bram Moolenaar0f873732019-12-05 20:28:46 +01002824 // Start keeping a divisor, for later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 divisor = 1;
2826 }
2827 else
2828 {
2829 if (!VIM_ISDIGIT(*str))
2830 break;
2831
2832 points *= 10;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002833 points += *str - L'0';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 divisor *= 10;
2835 }
2836 ++str;
2837 }
2838
2839 if (divisor == 0)
2840 divisor = 1;
2841
2842 if (printer_dc == NULL)
2843 {
2844 hwnd = GetDesktopWindow();
2845 hdc = GetWindowDC(hwnd);
2846 }
2847 else
2848 hdc = printer_dc;
2849
2850 pixels = MulDiv(points,
2851 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX),
2852 72 * divisor);
2853
2854 if (printer_dc == NULL)
2855 ReleaseDC(hwnd, hdc);
2856
2857 *end = str;
2858 return pixels;
2859}
2860
Yee Cheng Chin290b8872023-10-05 20:54:21 +02002861/*
2862 * Convert pixel into point size. This is a reverse of points_to_pixels.
2863 */
2864 static double
2865pixels_to_points(int pixels, int vertical, long_i pprinter_dc)
2866{
2867 double points = 0;
2868 HWND hwnd = (HWND)0;
2869 HDC hdc;
2870 HDC printer_dc = (HDC)pprinter_dc;
2871
2872 if (printer_dc == NULL)
2873 {
2874 hwnd = GetDesktopWindow();
2875 hdc = GetWindowDC(hwnd);
2876 }
2877 else
2878 hdc = printer_dc;
2879
2880 points = pixels * 72.0 / GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX);
2881 if (printer_dc == NULL)
2882 ReleaseDC(hwnd, hdc);
2883
2884 return points;
2885}
2886
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887 static int CALLBACK
2888font_enumproc(
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002889 ENUMLOGFONTW *elf,
2890 NEWTEXTMETRICW *ntm UNUSED,
2891 DWORD type UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 LPARAM lparam)
2893{
Bram Moolenaar0f873732019-12-05 20:28:46 +01002894 // Return value:
2895 // 0 = terminate now (monospace & ANSI)
2896 // 1 = continue, still no luck...
2897 // 2 = continue, but we have an acceptable LOGFONTW
2898 // (monospace, not ANSI)
2899 // We use these values, as EnumFontFamilies returns 1 if the
2900 // callback function is never called. So, we check the return as
2901 // 0 = perfect, 2 = OK, 1 = no good...
2902 // It's not pretty, but it works!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002904 LOGFONTW *lf = (LOGFONTW *)(lparam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002906# ifndef FEAT_PROPORTIONAL_FONTS
Bram Moolenaar0f873732019-12-05 20:28:46 +01002907 // Ignore non-monospace fonts without further ado
Bram Moolenaar071d4272004-06-13 20:20:40 +00002908 if ((ntm->tmPitchAndFamily & 1) != 0)
2909 return 1;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01002910# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911
Bram Moolenaar0f873732019-12-05 20:28:46 +01002912 // Remember this LOGFONTW as a "possible"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913 *lf = elf->elfLogFont;
2914
Bram Moolenaar0f873732019-12-05 20:28:46 +01002915 // Terminate the scan as soon as we find an ANSI font
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 if (lf->lfCharSet == ANSI_CHARSET
2917 || lf->lfCharSet == OEM_CHARSET
2918 || lf->lfCharSet == DEFAULT_CHARSET)
2919 return 0;
2920
Bram Moolenaar0f873732019-12-05 20:28:46 +01002921 // Continue the scan - we have a non-ANSI font
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 return 2;
2923}
2924
2925 static int
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002926init_logfont(LOGFONTW *lf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002927{
2928 int n;
2929 HWND hwnd = GetDesktopWindow();
2930 HDC hdc = GetWindowDC(hwnd);
2931
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002932 n = EnumFontFamiliesW(hdc,
2933 lf->lfFaceName,
2934 (FONTENUMPROCW)font_enumproc,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 (LPARAM)lf);
2936
2937 ReleaseDC(hwnd, hdc);
2938
Bram Moolenaar0f873732019-12-05 20:28:46 +01002939 // If we couldn't find a usable font, return failure
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940 if (n == 1)
2941 return FAIL;
2942
Bram Moolenaar0f873732019-12-05 20:28:46 +01002943 // Tidy up the rest of the LOGFONTW structure. We set to a basic
2944 // font - get_logfont() sets bold, italic, etc based on the user's
2945 // input.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002946 lf->lfHeight = current_font_height;
2947 lf->lfWidth = 0;
2948 lf->lfItalic = FALSE;
2949 lf->lfUnderline = FALSE;
2950 lf->lfStrikeOut = FALSE;
2951 lf->lfWeight = FW_NORMAL;
2952
Bram Moolenaar0f873732019-12-05 20:28:46 +01002953 // Return success
Bram Moolenaar071d4272004-06-13 20:20:40 +00002954 return OK;
2955}
2956
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002957/*
Yee Cheng Chin290b8872023-10-05 20:54:21 +02002958 * Call back for EnumFontFamiliesW in expand_font_enumproc.
2959 *
2960 */
2961 static int CALLBACK
2962expand_font_enumproc(
2963 ENUMLOGFONTW *elf,
2964 NEWTEXTMETRICW *ntm UNUSED,
2965 DWORD type UNUSED,
2966 LPARAM lparam)
2967{
2968 LOGFONTW *lf = (LOGFONTW*)elf;
2969
2970# ifndef FEAT_PROPORTIONAL_FONTS
2971 // Ignore non-monospace fonts without further ado
2972 if ((ntm->tmPitchAndFamily & 1) != 0)
2973 return 1;
2974# endif
2975
2976 // Filter only on ANSI. Otherwise will see a lot of random fonts that we
2977 // usually don't want.
2978 if (lf->lfCharSet != ANSI_CHARSET)
zeertzjqd9be94c2024-07-14 10:20:20 +02002979 return 1;
Yee Cheng Chin290b8872023-10-05 20:54:21 +02002980
2981 int (*add_match)(char_u *) = (int (*)(char_u *))lparam;
2982
2983 WCHAR *faceNameW = lf->lfFaceName;
2984 char_u *faceName = utf16_to_enc(faceNameW, NULL);
2985 if (!faceName)
2986 return 0;
2987
2988 add_match(faceName);
2989 vim_free(faceName);
2990
2991 return 1;
2992}
2993
2994/*
2995 * Cmdline expansion for setting 'guifont'. Will enumerate through all
2996 * monospace fonts for completion. If used after ':', will expand to possible
2997 * font configuration options like font sizes.
2998 *
2999 * This function has "gui" in its name because in some platforms (GTK) font
3000 * handling is done by the GUI code, whereas in Windows it's part of the
3001 * platform code.
3002 */
3003 void
3004gui_mch_expand_font(optexpand_T *args, void *param UNUSED, int (*add_match)(char_u *val))
3005{
3006 expand_T *xp = args->oe_xp;
3007 if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern-1) == ':')
3008 {
3009 char buf[30];
John Marriott51f6a782025-05-04 21:35:36 +02003010 int i;
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003011
3012 // Always fill in with the current font size as first option for
3013 // convenience. We simply round to the closest integer for simplicity.
zeertzjqd9be94c2024-07-14 10:20:20 +02003014 int font_height = (int)round(
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003015 pixels_to_points(-current_font_height, TRUE, (long_i)NULL));
3016 vim_snprintf(buf, ARRAY_LENGTH(buf), "h%d", font_height);
3017 add_match((char_u *)buf);
3018
3019 // Note: Keep this in sync with get_logfont(). Don't include 'c' and
3020 // 'q' as we fill in all the values below.
3021 static char *(p_gfn_win_opt_values[]) = {
3022 "h" , "w" , "W" , "b" , "i" , "u" , "s"};
John Marriott51f6a782025-05-04 21:35:36 +02003023 for (i = 0; i < (int)ARRAY_LENGTH(p_gfn_win_opt_values); i++)
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003024 add_match((char_u *)p_gfn_win_opt_values[i]);
3025
John Marriott51f6a782025-05-04 21:35:36 +02003026 for (i = 0; i < (int)ARRAY_LENGTH(charset_pairs); ++i)
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003027 {
John Marriott51f6a782025-05-04 21:35:36 +02003028 vim_snprintf(buf, sizeof(buf), "c%s", charset_pairs[i].name.string);
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003029 add_match((char_u *)buf);
3030 }
John Marriott51f6a782025-05-04 21:35:36 +02003031
3032 for (i = 0; i < (int)ARRAY_LENGTH(quality_pairs); ++i)
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003033 {
John Marriott51f6a782025-05-04 21:35:36 +02003034 vim_snprintf(buf, sizeof(buf), "q%s", quality_pairs[i].name.string);
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003035 add_match((char_u *)buf);
3036 }
3037 return;
3038 }
3039
3040 HWND hwnd = GetDesktopWindow();
3041 HDC hdc = GetWindowDC(hwnd);
3042
3043 EnumFontFamiliesW(hdc,
3044 NULL,
3045 (FONTENUMPROCW)expand_font_enumproc,
3046 (LPARAM)add_match);
3047
3048 ReleaseDC(hwnd, hdc);
3049}
3050
3051/*
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003052 * Compare a UTF-16 string and an ASCII string literally.
3053 * Only works all the code points are inside ASCII range.
3054 */
3055 static int
3056utf16ascncmp(const WCHAR *w, const char *p, size_t n)
3057{
3058 size_t i;
3059
3060 for (i = 0; i < n; i++)
3061 {
3062 if (w[i] == 0 || w[i] != p[i])
3063 return w[i] - p[i];
3064 }
3065 return 0;
3066}
3067
3068/*
Ken Takatad8cb1dd2024-01-12 18:09:43 +01003069 * Equivalent of GetDpiForSystem().
3070 */
3071 UINT WINAPI
3072vimGetDpiForSystem(void)
3073{
3074 HWND hwnd = GetDesktopWindow();
3075 HDC hdc = GetWindowDC(hwnd);
3076 UINT dpi = GetDeviceCaps(hdc, LOGPIXELSY);
3077 ReleaseDC(hwnd, hdc);
3078 return dpi;
3079}
3080
3081/*
3082 * Set default logfont based on current language.
3083 */
3084 static void
3085set_default_logfont(LOGFONTW *lf)
3086{
3087 // Default font name for current language on MS-Windows.
3088 // If not translated, falls back to "Consolas".
3089 // This must be a fixed-pitch font.
3090 const char *defaultfontname = N_("DefaultFontNameForWindows");
3091 char *fontname = _(defaultfontname);
3092
John Marriott51f6a782025-05-04 21:35:36 +02003093 if (STRCMP(fontname, defaultfontname) == 0)
Ken Takatad8cb1dd2024-01-12 18:09:43 +01003094 fontname = "Consolas";
3095
3096 *lf = s_lfDefault;
3097 lf->lfHeight = DEFAULT_FONT_HEIGHT * (int)vimGetDpiForSystem() / 96;
3098 if (current_font_height == 0)
3099 current_font_height = lf->lfHeight;
3100
3101 WCHAR *wfontname = enc_to_utf16((char_u*)fontname, NULL);
3102 if (wfontname != NULL)
3103 {
3104 wcscpy_s(lf->lfFaceName, LF_FACESIZE, wfontname);
3105 vim_free(wfontname);
3106 }
3107}
3108
3109/*
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00003110 * Get font info from "name" into logfont "lf".
3111 * Return OK for a valid name, FAIL otherwise.
3112 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003113 int
3114get_logfont(
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003115 LOGFONTW *lf,
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00003116 char_u *name,
3117 HDC printer_dc,
3118 int verbose)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003119{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003120 WCHAR *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121 int i;
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003122 int ret = FAIL;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003123 static LOGFONTW *lastlf = NULL;
3124 WCHAR *wname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003125
Ken Takatad8cb1dd2024-01-12 18:09:43 +01003126 set_default_logfont(lf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003127 if (name == NULL)
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00003128 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003129
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003130 wname = enc_to_utf16(name, NULL);
3131 if (wname == NULL)
3132 return FAIL;
3133
3134 if (wcscmp(wname, L"*") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003135 {
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003136# if defined(FEAT_GUI_MSWIN)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003137 CHOOSEFONTW cf;
Bram Moolenaar0f873732019-12-05 20:28:46 +01003138 // if name is "*", bring up std font dialog:
Bram Moolenaara80faa82020-04-12 19:37:17 +02003139 CLEAR_FIELD(cf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003140 cf.lStructSize = sizeof(cf);
3141 cf.hwndOwner = s_hwnd;
3142 cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY | CF_INITTOLOGFONTSTRUCT;
3143 if (lastlf != NULL)
3144 *lf = *lastlf;
3145 cf.lpLogFont = lf;
3146 cf.nFontType = 0 ; //REGULAR_FONTTYPE;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003147 if (ChooseFontW(&cf))
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003148 ret = OK;
Bram Moolenaar912bc4a2019-12-01 18:58:11 +01003149# endif
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003150 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151 }
3152
3153 /*
3154 * Split name up, it could be <name>:h<height>:w<width> etc.
3155 */
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003156 for (p = wname; *p && *p != L':'; p++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003158 if (p - wname + 1 >= LF_FACESIZE)
Bram Moolenaar0f873732019-12-05 20:28:46 +01003159 goto theend; // Name too long
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003160 lf->lfFaceName[p - wname] = *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161 }
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003162 if (p != wname)
3163 lf->lfFaceName[p - wname] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003164
Bram Moolenaar0f873732019-12-05 20:28:46 +01003165 // First set defaults
Ken Takatad8cb1dd2024-01-12 18:09:43 +01003166 lf->lfHeight = DEFAULT_FONT_HEIGHT * (int)vimGetDpiForSystem() / 96;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003167 lf->lfWidth = 0;
3168 lf->lfWeight = FW_NORMAL;
3169 lf->lfItalic = FALSE;
3170 lf->lfUnderline = FALSE;
3171 lf->lfStrikeOut = FALSE;
3172
3173 /*
3174 * If the font can't be found, try replacing '_' by ' '.
3175 */
3176 if (init_logfont(lf) == FAIL)
3177 {
3178 int did_replace = FALSE;
3179
3180 for (i = 0; lf->lfFaceName[i]; ++i)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003181 if (lf->lfFaceName[i] == L'_')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003182 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003183 lf->lfFaceName[i] = L' ';
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184 did_replace = TRUE;
3185 }
3186 if (!did_replace || init_logfont(lf) == FAIL)
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003187 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188 }
3189
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003190 while (*p == L':')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003191 p++;
3192
Bram Moolenaar0f873732019-12-05 20:28:46 +01003193 // Set the values found after ':'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 while (*p)
3195 {
3196 switch (*p++)
3197 {
Yee Cheng Chin290b8872023-10-05 20:54:21 +02003198 // Note: Keep this in sync with gui_mch_expand_font().
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003199 case L'h':
Bram Moolenaareb3593b2006-04-22 22:33:57 +00003200 lf->lfHeight = - points_to_pixels(p, &p, TRUE, (long_i)printer_dc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003201 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003202 case L'w':
Bram Moolenaareb3593b2006-04-22 22:33:57 +00003203 lf->lfWidth = points_to_pixels(p, &p, FALSE, (long_i)printer_dc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204 break;
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02003205 case L'W':
3206 lf->lfWeight = wcstol(p, &p, 10);
3207 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003208 case L'b':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003209 lf->lfWeight = FW_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003211 case L'i':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212 lf->lfItalic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003213 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003214 case L'u':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215 lf->lfUnderline = TRUE;
3216 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003217 case L's':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218 lf->lfStrikeOut = TRUE;
3219 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003220 case L'c':
John Marriott51f6a782025-05-04 21:35:36 +02003221 for (i = 0; i < (int)ARRAY_LENGTH(charset_pairs); ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222 {
John Marriott51f6a782025-05-04 21:35:36 +02003223 if (utf16ascncmp(p, (char *)charset_pairs[i].name.string,
3224 charset_pairs[i].name.length) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 {
John Marriott51f6a782025-05-04 21:35:36 +02003226 lf->lfCharSet = charset_pairs[i].charset;
3227 p += charset_pairs[i].name.length;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228 break;
3229 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230 }
John Marriott51f6a782025-05-04 21:35:36 +02003231
3232 if (i == (int)ARRAY_LENGTH(charset_pairs) && verbose)
3233 {
3234 char_u *s = utf16_to_enc(p, NULL);
3235 if (s != NULL)
3236 {
3237 semsg(_(e_illegal_str_name_str_in_font_name_str),
3238 "charset", s, name);
3239 vim_free(s);
3240 }
3241 }
3242 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003243 case L'q':
John Marriott51f6a782025-05-04 21:35:36 +02003244 for (i = 0; i < (int)ARRAY_LENGTH(quality_pairs); ++i)
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003245 {
John Marriott51f6a782025-05-04 21:35:36 +02003246 if (utf16ascncmp(p, (char *)quality_pairs[i].name.string,
3247 quality_pairs[i].name.length) == 0)
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003248 {
John Marriott51f6a782025-05-04 21:35:36 +02003249 lf->lfQuality = quality_pairs[i].quality;
3250 p += quality_pairs[i].name.length;
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003251 break;
3252 }
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003253 }
John Marriott51f6a782025-05-04 21:35:36 +02003254
3255 if (i == (int)ARRAY_LENGTH(quality_pairs) && verbose)
3256 {
3257 char_u *s = utf16_to_enc(p, NULL);
3258 if (s != NULL)
3259 {
3260 semsg(_(e_illegal_str_name_str_in_font_name_str),
3261 "quality", s, name);
3262 vim_free(s);
3263 }
3264 }
3265 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 default:
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00003267 if (verbose)
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00003268 semsg(_(e_illegal_char_nr_in_font_name_str), p[-1], name);
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003269 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270 }
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003271 while (*p == L':')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272 p++;
3273 }
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003274 ret = OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276theend:
Bram Moolenaar0f873732019-12-05 20:28:46 +01003277 // ron: init lastlf
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003278 if (ret == OK && printer_dc == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003279 {
3280 vim_free(lastlf);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02003281 lastlf = ALLOC_ONE(LOGFONTW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282 if (lastlf != NULL)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003283 mch_memmove(lastlf, lf, sizeof(LOGFONTW));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003284 }
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003285 vim_free(wname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003286
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003287 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003288}
3289
Bram Moolenaar0f873732019-12-05 20:28:46 +01003290#endif // defined(FEAT_GUI) || defined(FEAT_PRINTER)
Bram Moolenaarf12d9832016-01-29 21:11:25 +01003291
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003292#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaarf12d9832016-01-29 21:11:25 +01003293/*
3294 * Initialize the Winsock dll.
3295 */
3296 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003297channel_init_winsock(void)
Bram Moolenaarf12d9832016-01-29 21:11:25 +01003298{
3299 WSADATA wsaData;
3300 int wsaerr;
3301
3302 if (WSInitialized)
3303 return;
3304
3305 wsaerr = WSAStartup(MAKEWORD(2, 2), &wsaData);
3306 if (wsaerr == 0)
3307 WSInitialized = TRUE;
3308}
3309#endif