blob: 503d3d7d7bb9bf8fe0d9e40f17369394f4ef230e [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>
Bram Moolenaar82881492012-11-20 16:53:39 +010021#ifndef PROTO
22# include <process.h>
23#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000024
25#undef chdir
26#ifdef __GNUC__
27# ifndef __MINGW32__
28# include <dirent.h>
29# endif
30#else
31# include <direct.h>
32#endif
33
Bram Moolenaar82881492012-11-20 16:53:39 +010034#ifndef PROTO
Bram Moolenaar4f974752019-02-17 17:44:42 +010035# if defined(FEAT_TITLE) && !defined(FEAT_GUI_MSWIN)
Bram Moolenaar82881492012-11-20 16:53:39 +010036# include <shellapi.h>
37# endif
38
39# if defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)
40# include <dlgs.h>
Bram Moolenaarcea912a2016-10-12 14:20:24 +020041# include <winspool.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010042# include <commdlg.h>
Bram Moolenaarb04a98f2016-12-01 20:32:29 +010043# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000044
Bram Moolenaar82881492012-11-20 16:53:39 +010045#endif /* PROTO */
Bram Moolenaar071d4272004-06-13 20:20:40 +000046
47#ifdef __MINGW32__
48# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
49# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
50# endif
51# ifndef RIGHTMOST_BUTTON_PRESSED
52# define RIGHTMOST_BUTTON_PRESSED 0x0002
53# endif
54# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
55# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
56# endif
57# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
58# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
59# endif
60# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
61# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
62# endif
63
64/*
65 * EventFlags
66 */
67# ifndef MOUSE_MOVED
68# define MOUSE_MOVED 0x0001
69# endif
70# ifndef DOUBLE_CLICK
71# define DOUBLE_CLICK 0x0002
72# endif
73#endif
74
75/*
76 * When generating prototypes for Win32 on Unix, these lines make the syntax
77 * errors disappear. They do not need to be correct.
78 */
79#ifdef PROTO
80#define WINAPI
81#define WINBASEAPI
82typedef int BOOL;
83typedef int CALLBACK;
84typedef int COLORREF;
85typedef int CONSOLE_CURSOR_INFO;
86typedef int COORD;
87typedef int DWORD;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +010088typedef int ENUMLOGFONTW;
Bram Moolenaar071d4272004-06-13 20:20:40 +000089typedef int HANDLE;
90typedef int HDC;
91typedef int HFONT;
92typedef int HICON;
93typedef int HWND;
94typedef int INPUT_RECORD;
95typedef int KEY_EVENT_RECORD;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +010096typedef int LOGFONTW;
Bram Moolenaar071d4272004-06-13 20:20:40 +000097typedef int LPARAM;
98typedef int LPBOOL;
99typedef int LPCSTR;
100typedef int LPCWSTR;
Bram Moolenaarb9cdb372019-04-17 18:24:35 +0200101typedef int LPDWORD;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102typedef int LPSTR;
103typedef int LPTSTR;
Bram Moolenaarb9cdb372019-04-17 18:24:35 +0200104typedef int LPVOID;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000105typedef int LPWSTR;
106typedef int LRESULT;
107typedef int MOUSE_EVENT_RECORD;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +0100108typedef int NEWTEXTMETRICW;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109typedef int PACL;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200110typedef int PRINTDLGW;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111typedef int PSECURITY_DESCRIPTOR;
112typedef int PSID;
113typedef int SECURITY_INFORMATION;
114typedef int SHORT;
115typedef int SMALL_RECT;
116typedef int TEXTMETRIC;
117typedef int UINT;
118typedef int WCHAR;
Bram Moolenaarc447d8d2018-12-18 21:56:28 +0100119typedef int WNDENUMPROC;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000120typedef int WORD;
121typedef int WPARAM;
122typedef void VOID;
123#endif
124
125/* Record all output and all keyboard & mouse input */
126/* #define MCH_WRITE_DUMP */
127
128#ifdef MCH_WRITE_DUMP
129FILE* fdDump = NULL;
130#endif
131
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132#ifndef FEAT_GUI_MSWIN
133extern char g_szOrigTitle[];
134#endif
135
136#ifdef FEAT_GUI
137extern HWND s_hwnd;
138#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000139static HWND s_hwnd = 0; /* console window handle, set by GetConsoleHwnd() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000140#endif
141
Bram Moolenaar509ce2a2016-03-11 22:52:15 +0100142#ifdef FEAT_JOB_CHANNEL
Bram Moolenaarf12d9832016-01-29 21:11:25 +0100143int WSInitialized = FALSE; /* WinSock is initialized */
144#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145
146/* Don't generate prototypes here, because some systems do have these
147 * functions. */
148#if defined(__GNUC__) && !defined(PROTO)
149# ifndef __MINGW32__
150int _stricoll(char *a, char *b)
151{
152 // the ANSI-ish correct way is to use strxfrm():
153 char a_buff[512], b_buff[512]; // file names, so this is enough on Win32
154 strxfrm(a_buff, a, 512);
155 strxfrm(b_buff, b, 512);
156 return strcoll(a_buff, b_buff);
157}
158
159char * _fullpath(char *buf, char *fname, int len)
160{
161 LPTSTR toss;
162
163 return (char *)GetFullPathName(fname, len, buf, &toss);
164}
165# endif
166
Bram Moolenaaraf62ff32013-03-19 14:48:29 +0100167# if !defined(__MINGW32__) || (__GNUC__ < 4)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168int _chdrive(int drive)
169{
170 char temp [3] = "-:";
171 temp[0] = drive + 'A' - 1;
172 return !SetCurrentDirectory(temp);
173}
Bram Moolenaaraf62ff32013-03-19 14:48:29 +0100174# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175#else
176# ifdef __BORLANDC__
177/* being a more ANSI compliant compiler, BorlandC doesn't define _stricoll:
178 * but it does in BC 5.02! */
179# if __BORLANDC__ < 0x502
180int _stricoll(char *a, char *b)
181{
182# if 1
183 // this is fast but not correct:
184 return stricmp(a, b);
185# else
186 // the ANSI-ish correct way is to use strxfrm():
187 char a_buff[512], b_buff[512]; // file names, so this is enough on Win32
188 strxfrm(a_buff, a, 512);
189 strxfrm(b_buff, b, 512);
190 return strcoll(a_buff, b_buff);
191# endif
192}
193# endif
194# endif
195#endif
196
197
198#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
199/*
200 * GUI version of mch_exit().
201 * Shut down and exit with status `r'
202 * Careful: mch_exit() may be called before mch_init()!
203 */
204 void
205mch_exit(int r)
206{
Bram Moolenaar955f1982017-02-05 15:10:51 +0100207 exiting = TRUE;
208
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209 display_errors();
210
211 ml_close_all(TRUE); /* remove all memfiles */
212
213# ifdef FEAT_OLE
214 UninitOLE();
215# endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +0100216# ifdef FEAT_JOB_CHANNEL
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 if (WSInitialized)
218 {
219 WSInitialized = FALSE;
220 WSACleanup();
221 }
222# endif
223#ifdef DYNAMIC_GETTEXT
224 dyn_libintl_end();
225#endif
226
227 if (gui.in_use)
228 gui_exit(r);
Bram Moolenaar85c79d32007-02-20 01:59:20 +0000229
230#ifdef EXITFREE
231 free_all_mem();
232#endif
233
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234 exit(r);
235}
236
237#endif /* FEAT_GUI_MSWIN */
238
239
240/*
241 * Init the tables for toupper() and tolower().
242 */
243 void
244mch_early_init(void)
245{
246 int i;
247
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248 PlatformId();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249
250 /* Init the tables for toupper() and tolower() */
251 for (i = 0; i < 256; ++i)
252 toupper_tab[i] = tolower_tab[i] = i;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100253 CharUpperBuff((LPSTR)toupper_tab, 256);
254 CharLowerBuff((LPSTR)tolower_tab, 256);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255}
256
257
258/*
259 * Return TRUE if the input comes from a terminal, FALSE otherwise.
260 */
261 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100262mch_input_isatty(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000263{
264#ifdef FEAT_GUI_MSWIN
265 return OK; /* GUI always has a tty */
266#else
267 if (isatty(read_cmd_fd))
268 return TRUE;
269 return FALSE;
270#endif
271}
272
273#ifdef FEAT_TITLE
274/*
275 * mch_settitle(): set titlebar of our window
276 */
277 void
278mch_settitle(
279 char_u *title,
280 char_u *icon)
281{
282# ifdef FEAT_GUI_MSWIN
283 gui_mch_settitle(title, icon);
284# else
285 if (title != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000286 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200287 WCHAR *wp = enc_to_utf16(title, NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000288
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200289 if (wp == NULL)
290 return;
291
292 SetConsoleTitleW(wp);
293 vim_free(wp);
294 return;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000295 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000296# endif
297}
298
299
300/*
301 * Restore the window/icon title.
302 * which is one of:
Bram Moolenaar40385db2018-08-07 22:31:44 +0200303 * SAVE_RESTORE_TITLE: Just restore title
304 * SAVE_RESTORE_ICON: Just restore icon (which we don't have)
305 * SAVE_RESTORE_BOTH: Restore title and icon (which we don't have)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000306 */
307 void
Bram Moolenaar1266d672017-02-01 13:43:36 +0100308mch_restore_title(int which UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309{
310#ifndef FEAT_GUI_MSWIN
Bram Moolenaar1df52d72014-10-15 22:50:10 +0200311 SetConsoleTitle(g_szOrigTitle);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000312#endif
313}
314
315
316/*
317 * Return TRUE if we can restore the title (we can)
318 */
319 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100320mch_can_restore_title(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321{
322 return TRUE;
323}
324
325
326/*
327 * Return TRUE if we can restore the icon title (we can't)
328 */
329 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100330mch_can_restore_icon(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331{
332 return FALSE;
333}
334#endif /* FEAT_TITLE */
335
336
337/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000338 * Get absolute file name into buffer "buf" of length "len" bytes,
339 * turning all '/'s into '\\'s and getting the correct case of each component
340 * of the file name. Append a (back)slash to a directory name.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 * When 'shellslash' set do it the other way around.
342 * Return OK or FAIL.
343 */
344 int
345mch_FullName(
346 char_u *fname,
347 char_u *buf,
348 int len,
Bram Moolenaar1266d672017-02-01 13:43:36 +0100349 int force UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350{
351 int nResult = FAIL;
352
353#ifdef __BORLANDC__
354 if (*fname == NUL) /* Borland behaves badly here - make it consistent */
355 nResult = mch_dirname(buf, len);
356 else
357#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200359 WCHAR *wname;
360 WCHAR wbuf[MAX_PATH];
361 char_u *cname = NULL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000362
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200363 wname = enc_to_utf16(fname, NULL);
364 if (wname != NULL && _wfullpath(wbuf, wname, MAX_PATH) != NULL)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000365 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200366 cname = utf16_to_enc((short_u *)wbuf, NULL);
367 if (cname != NULL)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000368 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200369 vim_strncpy(buf, cname, len - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000370 nResult = OK;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200371 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000372 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200373 vim_free(wname);
374 vim_free(cname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000376
377#ifdef USE_FNAME_CASE
378 fname_case(buf, len);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000379#else
380 slash_adjust(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381#endif
382
383 return nResult;
384}
385
386
387/*
388 * Return TRUE if "fname" does not depend on the current directory.
389 */
390 int
391mch_isFullName(char_u *fname)
392{
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200393 /* WinNT and later can use _MAX_PATH wide characters for a pathname, which
394 * means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
395 * UTF-8. */
396 char szName[_MAX_PATH * 3 + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +0000397
398 /* A name like "d:/foo" and "//server/share" is absolute */
399 if ((fname[0] && fname[1] == ':' && (fname[2] == '/' || fname[2] == '\\'))
400 || (fname[0] == fname[1] && (fname[0] == '/' || fname[0] == '\\')))
401 return TRUE;
402
403 /* A name that can't be made absolute probably isn't absolute. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100404 if (mch_FullName(fname, (char_u *)szName, sizeof(szName) - 1, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000405 return FALSE;
406
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100407 return pathcmp((const char *)fname, (const char *)szName, -1) == 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408}
409
410/*
411 * Replace all slashes by backslashes.
412 * This used to be the other way around, but MS-DOS sometimes has problems
413 * with slashes (e.g. in a command name). We can't have mixed slashes and
414 * backslashes, because comparing file names will not work correctly. The
415 * commands that use a file name should try to avoid the need to type a
416 * backslash twice.
417 * When 'shellslash' set do it the other way around.
Bram Moolenaarb4f6a462015-10-13 19:43:17 +0200418 * When the path looks like a URL leave it unmodified.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419 */
420 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100421slash_adjust(char_u *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422{
Bram Moolenaarb4f6a462015-10-13 19:43:17 +0200423 if (path_with_url(p))
424 return;
Bram Moolenaar39d21e32017-08-05 23:09:31 +0200425
426 if (*p == '`')
427 {
Bram Moolenaar116a0f82017-08-07 21:17:57 +0200428 size_t len = STRLEN(p);
429
Bram Moolenaar39d21e32017-08-05 23:09:31 +0200430 /* don't replace backslash in backtick quoted strings */
Bram Moolenaar39d21e32017-08-05 23:09:31 +0200431 if (len > 2 && *(p + len - 1) == '`')
432 return;
433 }
434
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +0000435 while (*p)
436 {
437 if (*p == psepcN)
438 *p = psepc;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100439 MB_PTR_ADV(p);
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +0000440 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441}
442
Bram Moolenaar8767f522016-07-01 17:17:39 +0200443/* Use 64-bit stat functions if available. */
444#ifdef HAVE_STAT64
445# undef stat
446# undef _stat
447# undef _wstat
448# undef _fstat
449# define stat _stat64
450# define _stat _stat64
451# define _wstat _wstat64
452# define _fstat _fstat64
453#endif
454
Bram Moolenaar9d1685d2014-01-14 12:18:45 +0100455#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__MINGW32__)
Bram Moolenaar8962fda2013-09-29 19:05:21 +0200456# define OPEN_OH_ARGTYPE intptr_t
457#else
458# define OPEN_OH_ARGTYPE long
459#endif
460
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200461 static int
Bram Moolenaar8767f522016-07-01 17:17:39 +0200462wstat_symlink_aware(const WCHAR *name, stat_T *stp)
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200463{
Bram Moolenaara12a1612019-01-24 16:39:02 +0100464#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__MINGW32__)
Bram Moolenaarfce7b3d2016-01-19 19:00:32 +0100465 /* Work around for VC12 or earlier (and MinGW). _wstat() can't handle
466 * symlinks properly.
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200467 * VC9 or earlier: _wstat() doesn't support a symlink at all. It retrieves
468 * status of a symlink itself.
469 * VC10: _wstat() supports a symlink to a normal file, but it doesn't
Bram Moolenaarfce7b3d2016-01-19 19:00:32 +0100470 * support a symlink to a directory (always returns an error).
471 * VC11 and VC12: _wstat() doesn't return an error for a symlink to a
472 * directory, but it doesn't set S_IFDIR flag.
473 * MinGW: Same as VC9. */
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200474 int n;
475 BOOL is_symlink = FALSE;
476 HANDLE hFind, h;
477 DWORD attr = 0;
478 WIN32_FIND_DATAW findDataW;
479
480 hFind = FindFirstFileW(name, &findDataW);
481 if (hFind != INVALID_HANDLE_VALUE)
482 {
483 attr = findDataW.dwFileAttributes;
484 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
485 && (findDataW.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
486 is_symlink = TRUE;
487 FindClose(hFind);
488 }
489 if (is_symlink)
490 {
491 h = CreateFileW(name, FILE_READ_ATTRIBUTES,
492 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
493 OPEN_EXISTING,
494 (attr & FILE_ATTRIBUTE_DIRECTORY)
495 ? FILE_FLAG_BACKUP_SEMANTICS : 0,
496 NULL);
497 if (h != INVALID_HANDLE_VALUE)
498 {
499 int fd;
500
Bram Moolenaar8962fda2013-09-29 19:05:21 +0200501 fd = _open_osfhandle((OPEN_OH_ARGTYPE)h, _O_RDONLY);
Bram Moolenaar8767f522016-07-01 17:17:39 +0200502 n = _fstat(fd, (struct _stat *)stp);
Bram Moolenaarfce7b3d2016-01-19 19:00:32 +0100503 if ((n == 0) && (attr & FILE_ATTRIBUTE_DIRECTORY))
504 stp->st_mode = (stp->st_mode & ~S_IFREG) | S_IFDIR;
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200505 _close(fd);
506 return n;
507 }
508 }
Bram Moolenaara12a1612019-01-24 16:39:02 +0100509#endif
Bram Moolenaar8767f522016-07-01 17:17:39 +0200510 return _wstat(name, (struct _stat *)stp);
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200511}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512
513/*
514 * stat() can't handle a trailing '/' or '\', remove it first.
515 */
516 int
Bram Moolenaar8767f522016-07-01 17:17:39 +0200517vim_stat(const char *name, stat_T *stp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000518{
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200519 /* WinNT and later can use _MAX_PATH wide characters for a pathname, which
520 * means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
521 * UTF-8. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100522 char_u buf[_MAX_PATH * 3 + 1];
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100523 char_u *p;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200524 WCHAR *wp;
525 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200527 vim_strncpy((char_u *)buf, (char_u *)name, sizeof(buf) - 1);
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100528 p = buf + STRLEN(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 if (p > buf)
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100530 MB_PTR_BACK(buf, p);
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100531
532 /* Remove trailing '\\' except root path. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 if (p > buf && (*p == '\\' || *p == '/') && p[-1] != ':')
534 *p = NUL;
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100535
536 if ((buf[0] == '\\' && buf[1] == '\\') || (buf[0] == '/' && buf[1] == '/'))
537 {
538 /* UNC root path must be followed by '\\'. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100539 p = vim_strpbrk(buf + 2, (char_u *)"\\/");
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100540 if (p != NULL)
541 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100542 p = vim_strpbrk(p + 1, (char_u *)"\\/");
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100543 if (p == NULL)
544 STRCAT(buf, "\\");
545 }
546 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000547
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200548 wp = enc_to_utf16(buf, NULL);
549 if (wp == NULL)
550 return -1;
551
552 n = wstat_symlink_aware(wp, stp);
553 vim_free(wp);
554 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555}
556
557#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
558 void
Bram Moolenaar1266d672017-02-01 13:43:36 +0100559mch_settmode(int tmode UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560{
561 /* nothing to do */
562}
563
564 int
565mch_get_shellsize(void)
566{
567 /* never used */
568 return OK;
569}
570
571 void
572mch_set_shellsize(void)
573{
574 /* never used */
575}
576
577/*
578 * Rows and/or Columns has changed.
579 */
580 void
581mch_new_shellsize(void)
582{
583 /* never used */
584}
585
586#endif
587
588/*
589 * We have no job control, so fake it by starting a new shell.
590 */
591 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100592mch_suspend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593{
594 suspend_shell();
595}
596
597#if defined(USE_MCH_ERRMSG) || defined(PROTO)
598
599#ifdef display_errors
600# undef display_errors
601#endif
602
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +0100603#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604/*
605 * Display the saved error message(s).
606 */
607 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100608display_errors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000609{
610 char *p;
611
612 if (error_ga.ga_data != NULL)
613 {
614 /* avoid putting up a message box with blanks only */
615 for (p = (char *)error_ga.ga_data; *p; ++p)
616 if (!isspace(*p))
617 {
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000618 (void)gui_mch_dialog(
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000619 gui.starting ? VIM_INFO :
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000620 VIM_ERROR,
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000621 gui.starting ? (char_u *)_("Message") :
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000622 (char_u *)_("Error"),
Bram Moolenaar418f81b2016-02-16 20:12:02 +0100623 (char_u *)p, (char_u *)_("&Ok"),
624 1, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 break;
626 }
627 ga_clear(&error_ga);
628 }
629}
Bram Moolenaar9b5c1fc2019-02-14 14:08:04 +0100630#else
631 void
632display_errors(void)
633{
634 FlushFileBuffers(GetStdHandle(STD_ERROR_HANDLE));
635}
636#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637#endif
638
639
640/*
641 * Return TRUE if "p" contain a wildcard that can be expanded by
642 * dos_expandpath().
643 */
644 int
645mch_has_exp_wildcard(char_u *p)
646{
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100647 for ( ; *p; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 {
649 if (vim_strchr((char_u *)"?*[", *p) != NULL
650 || (*p == '~' && p[1] != NUL))
651 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 }
653 return FALSE;
654}
655
656/*
657 * Return TRUE if "p" contain a wildcard or a "~1" kind of thing (could be a
658 * shortened file name).
659 */
660 int
661mch_has_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 *)
666# ifdef VIM_BACKTICK
667 "?*$[`"
668# else
669 "?*$["
670# endif
671 , *p) != NULL
672 || (*p == '~' && p[1] != NUL))
673 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000674 }
675 return FALSE;
676}
677
678
679/*
680 * The normal _chdir() does not change the default drive. This one does.
681 * Returning 0 implies success; -1 implies failure.
682 */
683 int
684mch_chdir(char *path)
685{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200686 WCHAR *p;
687 int n;
688
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 if (path[0] == NUL) /* just checking... */
690 return -1;
691
Bram Moolenaara2974d72009-07-14 16:38:36 +0000692 if (p_verbose >= 5)
693 {
694 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100695 smsg("chdir(%s)", path);
Bram Moolenaara2974d72009-07-14 16:38:36 +0000696 verbose_leave();
697 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 if (isalpha(path[0]) && path[1] == ':') /* has a drive name */
699 {
700 /* If we can change to the drive, skip that part of the path. If we
701 * can't then the current directory may be invalid, try using chdir()
702 * with the whole path. */
703 if (_chdrive(TOLOWER_ASC(path[0]) - 'a' + 1) == 0)
704 path += 2;
705 }
706
707 if (*path == NUL) /* drive name only */
708 return 0;
709
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200710 p = enc_to_utf16((char_u *)path, NULL);
711 if (p == NULL)
712 return -1;
Bram Moolenaar15d0a8c2004-09-06 17:44:46 +0000713
Bram Moolenaar0eb035c2019-04-02 22:15:55 +0200714 n = _wchdir(p);
715 vim_free(p);
716 return n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717}
718
719
Bram Moolenaar071d4272004-06-13 20:20:40 +0000720#ifdef FEAT_GUI_MSWIN
721/*
722 * return non-zero if a character is available
723 */
724 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100725mch_char_avail(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000726{
727 /* never used */
728 return TRUE;
729}
Bram Moolenaare9c21ae2017-08-03 20:44:48 +0200730
731# if defined(FEAT_TERMINAL) || defined(PROTO)
732/*
733 * Check for any pending input or messages.
734 */
735 int
736mch_check_messages(void)
737{
738 /* TODO: check for messages */
739 return TRUE;
740}
741# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742#endif
743
744
745/*
746 * set screen mode, always fails.
747 */
748 int
Bram Moolenaar1266d672017-02-01 13:43:36 +0100749mch_screenmode(char_u *arg UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750{
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100751 emsg(_(e_screenmode));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752 return FAIL;
753}
754
755
756#if defined(FEAT_LIBCALL) || defined(PROTO)
757/*
758 * Call a DLL routine which takes either a string or int param
759 * and returns an allocated string.
760 * Return OK if it worked, FAIL if not.
761 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762typedef LPTSTR (*MYSTRPROCSTR)(LPTSTR);
763typedef LPTSTR (*MYINTPROCSTR)(int);
764typedef int (*MYSTRPROCINT)(LPTSTR);
765typedef int (*MYINTPROCINT)(int);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000766
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767/*
768 * Check if a pointer points to a valid NUL terminated string.
769 * Return the length of the string, including terminating NUL.
770 * Returns 0 for an invalid pointer, 1 for an empty string.
771 */
772 static size_t
773check_str_len(char_u *str)
774{
775 SYSTEM_INFO si;
776 MEMORY_BASIC_INFORMATION mbi;
777 size_t length = 0;
778 size_t i;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100779 const char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000780
781 /* get page size */
782 GetSystemInfo(&si);
783
784 /* get memory information */
785 if (VirtualQuery(str, &mbi, sizeof(mbi)))
786 {
787 /* pre cast these (typing savers) */
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000788 long_u dwStr = (long_u)str;
789 long_u dwBaseAddress = (long_u)mbi.BaseAddress;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000790
791 /* get start address of page that str is on */
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000792 long_u strPage = dwStr - (dwStr - dwBaseAddress) % si.dwPageSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000793
794 /* get length from str to end of page */
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000795 long_u pageLength = si.dwPageSize - (dwStr - strPage);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000796
Bram Moolenaar442b4222010-05-24 21:34:22 +0200797 for (p = str; !IsBadReadPtr(p, (UINT)pageLength);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798 p += pageLength, pageLength = si.dwPageSize)
799 for (i = 0; i < pageLength; ++i, ++length)
800 if (p[i] == NUL)
801 return length + 1;
802 }
803
804 return 0;
805}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000806
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200807/*
808 * Passed to do_in_runtimepath() to load a vim.ico file.
809 */
810 static void
811mch_icon_load_cb(char_u *fname, void *cookie)
812{
813 HANDLE *h = (HANDLE *)cookie;
814
815 *h = LoadImage(NULL,
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100816 (LPSTR)fname,
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200817 IMAGE_ICON,
818 64,
819 64,
820 LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
821}
822
823/*
824 * Try loading an icon file from 'runtimepath'.
825 */
826 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100827mch_icon_load(HANDLE *iconp)
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200828{
829 return do_in_runtimepath((char_u *)"bitmaps/vim.ico",
Bram Moolenaar7f8989d2016-03-12 22:11:39 +0100830 0, mch_icon_load_cb, iconp);
Bram Moolenaarcddc91c2014-09-23 21:53:41 +0200831}
832
Bram Moolenaar071d4272004-06-13 20:20:40 +0000833 int
834mch_libcall(
835 char_u *libname,
836 char_u *funcname,
837 char_u *argstring, /* NULL when using a argint */
838 int argint,
839 char_u **string_result,/* NULL when using number_result */
840 int *number_result)
841{
842 HINSTANCE hinstLib;
843 MYSTRPROCSTR ProcAdd;
844 MYINTPROCSTR ProcAddI;
845 char_u *retval_str = NULL;
846 int retval_int = 0;
847 size_t len;
848
849 BOOL fRunTimeLinkSuccess = FALSE;
850
851 // Get a handle to the DLL module.
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100852 hinstLib = vimLoadLib((char *)libname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000853
854 // If the handle is valid, try to get the function address.
855 if (hinstLib != NULL)
856 {
857#ifdef HAVE_TRY_EXCEPT
858 __try
859 {
860#endif
861 if (argstring != NULL)
862 {
863 /* Call with string argument */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100864 ProcAdd = (MYSTRPROCSTR)GetProcAddress(hinstLib, (LPCSTR)funcname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000865 if ((fRunTimeLinkSuccess = (ProcAdd != NULL)) != 0)
866 {
867 if (string_result == NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100868 retval_int = ((MYSTRPROCINT)ProcAdd)((LPSTR)argstring);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869 else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100870 retval_str = (char_u *)(ProcAdd)((LPSTR)argstring);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871 }
872 }
873 else
874 {
875 /* Call with number argument */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100876 ProcAddI = (MYINTPROCSTR) GetProcAddress(hinstLib, (LPCSTR)funcname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000877 if ((fRunTimeLinkSuccess = (ProcAddI != NULL)) != 0)
878 {
879 if (string_result == NULL)
880 retval_int = ((MYINTPROCINT)ProcAddI)(argint);
881 else
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +0100882 retval_str = (char_u *)(ProcAddI)(argint);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000883 }
884 }
885
886 // Save the string before we free the library.
887 // Assume that a "1" result is an illegal pointer.
888 if (string_result == NULL)
889 *number_result = retval_int;
890 else if (retval_str != NULL
Bram Moolenaarcf7164a2016-02-20 13:55:06 +0100891 && (len = check_str_len(retval_str)) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000892 {
893 *string_result = lalloc((long_u)len, TRUE);
894 if (*string_result != NULL)
895 mch_memmove(*string_result, retval_str, len);
896 }
897
898#ifdef HAVE_TRY_EXCEPT
899 }
900 __except(EXCEPTION_EXECUTE_HANDLER)
901 {
902 if (GetExceptionCode() == EXCEPTION_STACK_OVERFLOW)
903 RESETSTKOFLW();
904 fRunTimeLinkSuccess = 0;
905 }
906#endif
907
908 // Free the DLL module.
909 (void)FreeLibrary(hinstLib);
910 }
911
912 if (!fRunTimeLinkSuccess)
913 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100914 semsg(_(e_libcall), funcname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000915 return FAIL;
916 }
917
918 return OK;
919}
920#endif
921
Bram Moolenaar071d4272004-06-13 20:20:40 +0000922/*
923 * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
924 */
925 void
Bram Moolenaar1266d672017-02-01 13:43:36 +0100926DumpPutS(const char *psz UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000927{
928# ifdef MCH_WRITE_DUMP
929 if (fdDump)
930 {
931 fputs(psz, fdDump);
932 if (psz[strlen(psz) - 1] != '\n')
933 fputc('\n', fdDump);
934 fflush(fdDump);
935 }
936# endif
937}
938
939#ifdef _DEBUG
940
941void __cdecl
942Trace(
943 char *pszFormat,
944 ...)
945{
946 CHAR szBuff[2048];
947 va_list args;
948
949 va_start(args, pszFormat);
950 vsprintf(szBuff, pszFormat, args);
951 va_end(args);
952
953 OutputDebugString(szBuff);
954}
955
956#endif //_DEBUG
957
Bram Moolenaar843ee412004-06-30 16:16:41 +0000958#if !defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200959# ifdef FEAT_TITLE
Bram Moolenaar071d4272004-06-13 20:20:40 +0000960extern HWND g_hWnd; /* This is in os_win32.c. */
961# endif
962
963/*
964 * Showing the printer dialog is tricky since we have no GUI
965 * window to parent it. The following routines are needed to
966 * get the window parenting and Z-order to work properly.
967 */
968 static void
969GetConsoleHwnd(void)
970{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971 /* Skip if it's already set. */
972 if (s_hwnd != 0)
973 return;
974
Bram Moolenaarcea912a2016-10-12 14:20:24 +0200975# ifdef FEAT_TITLE
Bram Moolenaar071d4272004-06-13 20:20:40 +0000976 /* Window handle may have been found by init code (Windows NT only) */
977 if (g_hWnd != 0)
978 {
979 s_hwnd = g_hWnd;
980 return;
981 }
982# endif
983
Bram Moolenaare1ed53f2019-02-12 23:12:37 +0100984 s_hwnd = GetConsoleWindow();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000985}
Bram Moolenaar843ee412004-06-30 16:16:41 +0000986
987/*
988 * Console implementation of ":winpos".
989 */
990 int
991mch_get_winpos(int *x, int *y)
992{
993 RECT rect;
994
995 GetConsoleHwnd();
996 GetWindowRect(s_hwnd, &rect);
997 *x = rect.left;
998 *y = rect.top;
999 return OK;
1000}
1001
1002/*
1003 * Console implementation of ":winpos x y".
1004 */
1005 void
1006mch_set_winpos(int x, int y)
1007{
1008 GetConsoleHwnd();
1009 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1010 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1011}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001012#endif
1013
1014#if (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) || defined(PROTO)
1015
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016/*=================================================================
1017 * Win32 printer stuff
1018 */
1019
1020static HFONT prt_font_handles[2][2][2];
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001021static PRINTDLGW prt_dlg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022static const int boldface[2] = {FW_REGULAR, FW_BOLD};
1023static TEXTMETRIC prt_tm;
1024static int prt_line_height;
1025static int prt_number_width;
1026static int prt_left_margin;
1027static int prt_right_margin;
1028static int prt_top_margin;
1029static char_u szAppName[] = TEXT("VIM");
1030static HWND hDlgPrint;
1031static int *bUserAbort = NULL;
1032static char_u *prt_name = NULL;
1033
1034/* Defines which are also in vim.rc. */
1035#define IDC_BOX1 400
1036#define IDC_PRINTTEXT1 401
1037#define IDC_PRINTTEXT2 402
1038#define IDC_PROGRESS 403
1039
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001040 static BOOL
1041vimSetDlgItemText(HWND hDlg, int nIDDlgItem, char_u *s)
1042{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001043 WCHAR *wp;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001044 BOOL ret;
1045
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001046 wp = enc_to_utf16(s, NULL);
1047 if (wp == NULL)
1048 return FALSE;
1049
1050 ret = SetDlgItemTextW(hDlg, nIDDlgItem, wp);
1051 vim_free(wp);
1052 return ret;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001053}
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001054
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055/*
1056 * Convert BGR to RGB for Windows GDI calls
1057 */
1058 static COLORREF
1059swap_me(COLORREF colorref)
1060{
1061 int temp;
1062 char *ptr = (char *)&colorref;
1063
1064 temp = *(ptr);
1065 *(ptr ) = *(ptr + 2);
1066 *(ptr + 2) = temp;
1067 return colorref;
1068}
1069
Bram Moolenaared39e1d2008-08-09 17:55:22 +00001070/* Attempt to make this work for old and new compilers */
Bram Moolenaar9f733d12011-09-21 20:09:42 +02001071#if !defined(_WIN64) && (!defined(_MSC_VER) || _MSC_VER < 1300)
Bram Moolenaared39e1d2008-08-09 17:55:22 +00001072# define PDP_RETVAL BOOL
1073#else
1074# define PDP_RETVAL INT_PTR
1075#endif
1076
Bram Moolenaared39e1d2008-08-09 17:55:22 +00001077 static PDP_RETVAL CALLBACK
Bram Moolenaar1266d672017-02-01 13:43:36 +01001078PrintDlgProc(
1079 HWND hDlg,
1080 UINT message,
1081 WPARAM wParam UNUSED,
1082 LPARAM lParam UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001083{
1084#ifdef FEAT_GETTEXT
1085 NONCLIENTMETRICS nm;
1086 static HFONT hfont;
1087#endif
1088
1089 switch (message)
1090 {
1091 case WM_INITDIALOG:
1092#ifdef FEAT_GETTEXT
1093 nm.cbSize = sizeof(NONCLIENTMETRICS);
1094 if (SystemParametersInfo(
1095 SPI_GETNONCLIENTMETRICS,
1096 sizeof(NONCLIENTMETRICS),
1097 &nm,
1098 0))
1099 {
1100 char buff[MAX_PATH];
1101 int i;
1102
1103 /* Translate the dialog texts */
1104 hfont = CreateFontIndirect(&nm.lfMessageFont);
1105 for (i = IDC_PRINTTEXT1; i <= IDC_PROGRESS; i++)
1106 {
1107 SendDlgItemMessage(hDlg, i, WM_SETFONT, (WPARAM)hfont, 1);
1108 if (GetDlgItemText(hDlg,i, buff, sizeof(buff)))
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001109 vimSetDlgItemText(hDlg,i, (char_u *)_(buff));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 }
1111 SendDlgItemMessage(hDlg, IDCANCEL,
1112 WM_SETFONT, (WPARAM)hfont, 1);
1113 if (GetDlgItemText(hDlg,IDCANCEL, buff, sizeof(buff)))
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001114 vimSetDlgItemText(hDlg,IDCANCEL, (char_u *)_(buff));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001115 }
1116#endif
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001117 SetWindowText(hDlg, (LPCSTR)szAppName);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001118 if (prt_name != NULL)
1119 {
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001120 vimSetDlgItemText(hDlg, IDC_PRINTTEXT2, (char_u *)prt_name);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001121 VIM_CLEAR(prt_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001122 }
1123 EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_CLOSE, MF_GRAYED);
1124#ifndef FEAT_GUI
1125 BringWindowToTop(s_hwnd);
1126#endif
1127 return TRUE;
1128
1129 case WM_COMMAND:
1130 *bUserAbort = TRUE;
1131 EnableWindow(GetParent(hDlg), TRUE);
1132 DestroyWindow(hDlg);
1133 hDlgPrint = NULL;
1134#ifdef FEAT_GETTEXT
1135 DeleteObject(hfont);
1136#endif
1137 return TRUE;
1138 }
1139 return FALSE;
1140}
1141
1142 static BOOL CALLBACK
Bram Moolenaar1266d672017-02-01 13:43:36 +01001143AbortProc(HDC hdcPrn UNUSED, int iCode UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144{
1145 MSG msg;
1146
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02001147 while (!*bUserAbort && pPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148 {
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02001149 if (!hDlgPrint || !pIsDialogMessage(hDlgPrint, &msg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150 {
1151 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02001152 pDispatchMessage(&msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001153 }
1154 }
1155 return !*bUserAbort;
1156}
1157
1158#ifndef FEAT_GUI
1159
Bram Moolenaar26fdd7d2011-11-30 13:42:44 +01001160 static UINT_PTR CALLBACK
Bram Moolenaar071d4272004-06-13 20:20:40 +00001161PrintHookProc(
1162 HWND hDlg, // handle to dialog box
1163 UINT uiMsg, // message identifier
1164 WPARAM wParam, // message parameter
1165 LPARAM lParam // message parameter
1166 )
1167{
1168 HWND hwndOwner;
1169 RECT rc, rcDlg, rcOwner;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001170 PRINTDLGW *pPD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171
1172 if (uiMsg == WM_INITDIALOG)
1173 {
1174 // Get the owner window and dialog box rectangles.
1175 if ((hwndOwner = GetParent(hDlg)) == NULL)
1176 hwndOwner = GetDesktopWindow();
1177
1178 GetWindowRect(hwndOwner, &rcOwner);
1179 GetWindowRect(hDlg, &rcDlg);
1180 CopyRect(&rc, &rcOwner);
1181
1182 // Offset the owner and dialog box rectangles so that
1183 // right and bottom values represent the width and
1184 // height, and then offset the owner again to discard
1185 // space taken up by the dialog box.
1186
1187 OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1188 OffsetRect(&rc, -rc.left, -rc.top);
1189 OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
1190
1191 // The new position is the sum of half the remaining
1192 // space and the owner's original position.
1193
1194 SetWindowPos(hDlg,
1195 HWND_TOP,
1196 rcOwner.left + (rc.right / 2),
1197 rcOwner.top + (rc.bottom / 2),
1198 0, 0, // ignores size arguments
1199 SWP_NOSIZE);
1200
1201 /* tackle the printdlg copiesctrl problem */
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001202 pPD = (PRINTDLGW *)lParam;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001203 pPD->nCopies = (WORD)pPD->lCustData;
1204 SetDlgItemInt( hDlg, edt3, pPD->nCopies, FALSE );
1205 /* Bring the window to top */
1206 BringWindowToTop(GetParent(hDlg));
1207 SetForegroundWindow(hDlg);
1208 }
1209
1210 return FALSE;
1211}
1212#endif
1213
1214 void
1215mch_print_cleanup(void)
1216{
1217 int pifItalic;
1218 int pifBold;
1219 int pifUnderline;
1220
1221 for (pifBold = 0; pifBold <= 1; pifBold++)
1222 for (pifItalic = 0; pifItalic <= 1; pifItalic++)
1223 for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
1224 DeleteObject(prt_font_handles[pifBold][pifItalic][pifUnderline]);
1225
1226 if (prt_dlg.hDC != NULL)
1227 DeleteDC(prt_dlg.hDC);
1228 if (!*bUserAbort)
1229 SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
1230}
1231
1232 static int
1233to_device_units(int idx, int dpi, int physsize, int offset, int def_number)
1234{
1235 int ret = 0;
1236 int u;
1237 int nr;
1238
1239 u = prt_get_unit(idx);
1240 if (u == PRT_UNIT_NONE)
1241 {
1242 u = PRT_UNIT_PERC;
1243 nr = def_number;
1244 }
1245 else
1246 nr = printer_opts[idx].number;
1247
1248 switch (u)
1249 {
1250 case PRT_UNIT_PERC:
1251 ret = (physsize * nr) / 100;
1252 break;
1253 case PRT_UNIT_INCH:
1254 ret = (nr * dpi);
1255 break;
1256 case PRT_UNIT_MM:
1257 ret = (nr * 10 * dpi) / 254;
1258 break;
1259 case PRT_UNIT_POINT:
1260 ret = (nr * 10 * dpi) / 720;
1261 break;
1262 }
1263
1264 if (ret < offset)
1265 return 0;
1266 else
1267 return ret - offset;
1268}
1269
1270 static int
1271prt_get_cpl(void)
1272{
1273 int hr;
1274 int phyw;
1275 int dvoff;
1276 int rev_offset;
1277 int dpi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278
1279 GetTextMetrics(prt_dlg.hDC, &prt_tm);
1280 prt_line_height = prt_tm.tmHeight + prt_tm.tmExternalLeading;
1281
1282 hr = GetDeviceCaps(prt_dlg.hDC, HORZRES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001283 phyw = GetDeviceCaps(prt_dlg.hDC, PHYSICALWIDTH);
1284 dvoff = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285 dpi = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSX);
1286
1287 rev_offset = phyw - (dvoff + hr);
1288
1289 prt_left_margin = to_device_units(OPT_PRINT_LEFT, dpi, phyw, dvoff, 10);
1290 if (prt_use_number())
1291 {
1292 prt_number_width = PRINT_NUMBER_WIDTH * prt_tm.tmAveCharWidth;
1293 prt_left_margin += prt_number_width;
1294 }
1295 else
1296 prt_number_width = 0;
1297
1298 prt_right_margin = hr - to_device_units(OPT_PRINT_RIGHT, dpi, phyw,
1299 rev_offset, 5);
1300
1301 return (prt_right_margin - prt_left_margin) / prt_tm.tmAveCharWidth;
1302}
1303
1304 static int
1305prt_get_lpp(void)
1306{
1307 int vr;
1308 int phyw;
1309 int dvoff;
1310 int rev_offset;
1311 int bottom_margin;
1312 int dpi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313
1314 vr = GetDeviceCaps(prt_dlg.hDC, VERTRES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315 phyw = GetDeviceCaps(prt_dlg.hDC, PHYSICALHEIGHT);
1316 dvoff = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETY);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 dpi = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSY);
1318
1319 rev_offset = phyw - (dvoff + vr);
1320
1321 prt_top_margin = to_device_units(OPT_PRINT_TOP, dpi, phyw, dvoff, 5);
1322
1323 /* adjust top margin if there is a header */
1324 prt_top_margin += prt_line_height * prt_header_height();
1325
1326 bottom_margin = vr - to_device_units(OPT_PRINT_BOT, dpi, phyw,
1327 rev_offset, 5);
1328
1329 return (bottom_margin - prt_top_margin) / prt_line_height;
1330}
1331
1332 int
1333mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
1334{
1335 static HGLOBAL stored_dm = NULL;
1336 static HGLOBAL stored_devn = NULL;
1337 static int stored_nCopies = 1;
1338 static int stored_nFlags = 0;
1339
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001340 LOGFONTW fLogFont;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 int pifItalic;
1342 int pifBold;
1343 int pifUnderline;
1344
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001345 DEVMODEW *mem;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 DEVNAMES *devname;
1347 int i;
1348
1349 bUserAbort = &(psettings->user_abort);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001350 vim_memset(&prt_dlg, 0, sizeof(PRINTDLGW));
1351 prt_dlg.lStructSize = sizeof(PRINTDLGW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001352#ifndef FEAT_GUI
1353 GetConsoleHwnd(); /* get value of s_hwnd */
1354#endif
1355 prt_dlg.hwndOwner = s_hwnd;
1356 prt_dlg.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
1357 if (!forceit)
1358 {
1359 prt_dlg.hDevMode = stored_dm;
1360 prt_dlg.hDevNames = stored_devn;
1361 prt_dlg.lCustData = stored_nCopies; // work around bug in print dialog
1362#ifndef FEAT_GUI
1363 /*
1364 * Use hook to prevent console window being sent to back
1365 */
1366 prt_dlg.lpfnPrintHook = PrintHookProc;
1367 prt_dlg.Flags |= PD_ENABLEPRINTHOOK;
1368#endif
1369 prt_dlg.Flags |= stored_nFlags;
1370 }
1371
1372 /*
1373 * If bang present, return default printer setup with no dialog
1374 * never show dialog if we are running over telnet
1375 */
1376 if (forceit
1377#ifndef FEAT_GUI
1378 || !term_console
1379#endif
1380 )
1381 {
1382 prt_dlg.Flags |= PD_RETURNDEFAULT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 /*
1384 * MSDN suggests setting the first parameter to WINSPOOL for
1385 * NT, but NULL appears to work just as well.
1386 */
1387 if (*p_pdev != NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001388 prt_dlg.hDC = CreateDC(NULL, (LPCSTR)p_pdev, NULL, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390 {
1391 prt_dlg.Flags |= PD_RETURNDEFAULT;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001392 if (PrintDlgW(&prt_dlg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 goto init_fail_dlg;
1394 }
1395 }
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001396 else if (PrintDlgW(&prt_dlg) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 goto init_fail_dlg;
1398 else
1399 {
1400 /*
1401 * keep the previous driver context
1402 */
1403 stored_dm = prt_dlg.hDevMode;
1404 stored_devn = prt_dlg.hDevNames;
1405 stored_nFlags = prt_dlg.Flags;
1406 stored_nCopies = prt_dlg.nCopies;
1407 }
1408
1409 if (prt_dlg.hDC == NULL)
1410 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001411 emsg(_("E237: Printer selection failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 mch_print_cleanup();
1413 return FALSE;
1414 }
1415
1416 /* Not all printer drivers report the support of color (or grey) in the
1417 * same way. Let's set has_color if there appears to be some way to print
1418 * more than B&W. */
1419 i = GetDeviceCaps(prt_dlg.hDC, NUMCOLORS);
1420 psettings->has_color = (GetDeviceCaps(prt_dlg.hDC, BITSPIXEL) > 1
1421 || GetDeviceCaps(prt_dlg.hDC, PLANES) > 1
1422 || i > 2 || i == -1);
1423
1424 /* Ensure all font styles are baseline aligned */
1425 SetTextAlign(prt_dlg.hDC, TA_BASELINE|TA_LEFT);
1426
1427 /*
1428 * On some windows systems the nCopies parameter is not
1429 * passed back correctly. It must be retrieved from the
1430 * hDevMode struct.
1431 */
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001432 mem = (DEVMODEW *)GlobalLock(prt_dlg.hDevMode);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433 if (mem != NULL)
1434 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435 if (mem->dmCopies != 1)
1436 stored_nCopies = mem->dmCopies;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 if ((mem->dmFields & DM_DUPLEX) && (mem->dmDuplex & ~DMDUP_SIMPLEX))
1438 psettings->duplex = TRUE;
1439 if ((mem->dmFields & DM_COLOR) && (mem->dmColor & DMCOLOR_COLOR))
1440 psettings->has_color = TRUE;
1441 }
1442 GlobalUnlock(prt_dlg.hDevMode);
1443
1444 devname = (DEVNAMES *)GlobalLock(prt_dlg.hDevNames);
1445 if (devname != 0)
1446 {
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001447 WCHAR *wprinter_name = (WCHAR *)devname + devname->wDeviceOffset;
1448 WCHAR *wport_name = (WCHAR *)devname + devname->wOutputOffset;
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001449 char_u *text = (char_u *)_("to %s on %s");
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001450 char_u *printer_name = utf16_to_enc(wprinter_name, NULL);
1451 char_u *port_name = utf16_to_enc(wport_name, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001452
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001453 if (printer_name != NULL && port_name != NULL)
1454 prt_name = alloc((unsigned)(STRLEN(printer_name)
1455 + STRLEN(port_name) + STRLEN(text)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 if (prt_name != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001457 wsprintf((char *)prt_name, (const char *)text,
1458 printer_name, port_name);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001459 vim_free(printer_name);
1460 vim_free(port_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001461 }
1462 GlobalUnlock(prt_dlg.hDevNames);
1463
1464 /*
1465 * Initialise the font according to 'printfont'
1466 */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02001467 vim_memset(&fLogFont, 0, sizeof(fLogFont));
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001468 if (get_logfont(&fLogFont, p_pfn, prt_dlg.hDC, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001470 semsg(_("E613: Unknown printer font: %s"), p_pfn);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471 mch_print_cleanup();
1472 return FALSE;
1473 }
1474
1475 for (pifBold = 0; pifBold <= 1; pifBold++)
1476 for (pifItalic = 0; pifItalic <= 1; pifItalic++)
1477 for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
1478 {
1479 fLogFont.lfWeight = boldface[pifBold];
1480 fLogFont.lfItalic = pifItalic;
1481 fLogFont.lfUnderline = pifUnderline;
1482 prt_font_handles[pifBold][pifItalic][pifUnderline]
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01001483 = CreateFontIndirectW(&fLogFont);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 }
1485
1486 SetBkMode(prt_dlg.hDC, OPAQUE);
1487 SelectObject(prt_dlg.hDC, prt_font_handles[0][0][0]);
1488
1489 /*
1490 * Fill in the settings struct
1491 */
1492 psettings->chars_per_line = prt_get_cpl();
1493 psettings->lines_per_page = prt_get_lpp();
Bram Moolenaar7ddc6422014-09-27 11:18:19 +02001494 if (prt_dlg.Flags & PD_USEDEVMODECOPIESANDCOLLATE)
1495 {
1496 psettings->n_collated_copies = (prt_dlg.Flags & PD_COLLATE)
1497 ? prt_dlg.nCopies : 1;
1498 psettings->n_uncollated_copies = (prt_dlg.Flags & PD_COLLATE)
1499 ? 1 : prt_dlg.nCopies;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500
Bram Moolenaar7ddc6422014-09-27 11:18:19 +02001501 if (psettings->n_collated_copies == 0)
1502 psettings->n_collated_copies = 1;
1503
1504 if (psettings->n_uncollated_copies == 0)
1505 psettings->n_uncollated_copies = 1;
Bram Moolenaarb04a98f2016-12-01 20:32:29 +01001506 }
1507 else
1508 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001509 psettings->n_collated_copies = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 psettings->n_uncollated_copies = 1;
Bram Moolenaar7ddc6422014-09-27 11:18:19 +02001511 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512
1513 psettings->jobname = jobname;
1514
1515 return TRUE;
1516
1517init_fail_dlg:
1518 {
1519 DWORD err = CommDlgExtendedError();
1520
1521 if (err)
1522 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001523 char_u *buf;
1524
1525 /* I suspect FormatMessage() doesn't work for values returned by
1526 * CommDlgExtendedError(). What does? */
1527 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
1528 FORMAT_MESSAGE_FROM_SYSTEM |
1529 FORMAT_MESSAGE_IGNORE_INSERTS,
1530 NULL, err, 0, (LPTSTR)(&buf), 0, NULL);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001531 semsg(_("E238: Print error: %s"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001532 buf == NULL ? (char_u *)_("Unknown") : buf);
1533 LocalFree((LPVOID)(buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001534 }
1535 else
1536 msg_clr_eos(); /* Maybe canceled */
1537
1538 mch_print_cleanup();
1539 return FALSE;
1540 }
1541}
1542
1543
1544 int
1545mch_print_begin(prt_settings_T *psettings)
1546{
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001547 int ret = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 char szBuffer[300];
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001549 WCHAR *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550
1551 hDlgPrint = CreateDialog(GetModuleHandle(NULL), TEXT("PrintDlgBox"),
1552 prt_dlg.hwndOwner, PrintDlgProc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 SetAbortProc(prt_dlg.hDC, AbortProc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 wsprintf(szBuffer, _("Printing '%s'"), gettail(psettings->jobname));
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001555 vimSetDlgItemText(hDlgPrint, IDC_PRINTTEXT1, (char_u *)szBuffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001557 wp = enc_to_utf16(psettings->jobname, NULL);
Bram Moolenaar2290b1f2018-05-13 17:30:45 +02001558 if (wp != NULL)
1559 {
1560 DOCINFOW di;
1561
1562 vim_memset(&di, 0, sizeof(di));
1563 di.cbSize = sizeof(di);
1564 di.lpszDocName = wp;
1565 ret = StartDocW(prt_dlg.hDC, &di);
1566 vim_free(wp);
1567 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001568
1569#ifdef FEAT_GUI
1570 /* Give focus back to main window (when using MDI). */
1571 SetFocus(s_hwnd);
1572#endif
1573
1574 return (ret > 0);
1575}
1576
1577 void
Bram Moolenaar1266d672017-02-01 13:43:36 +01001578mch_print_end(prt_settings_T *psettings UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001579{
1580 EndDoc(prt_dlg.hDC);
1581 if (!*bUserAbort)
1582 SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
1583}
1584
1585 int
1586mch_print_end_page(void)
1587{
1588 return (EndPage(prt_dlg.hDC) > 0);
1589}
1590
1591 int
1592mch_print_begin_page(char_u *msg)
1593{
1594 if (msg != NULL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01001595 vimSetDlgItemText(hDlgPrint, IDC_PROGRESS, msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596 return (StartPage(prt_dlg.hDC) > 0);
1597}
1598
1599 int
1600mch_print_blank_page(void)
1601{
1602 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
1603}
1604
1605static int prt_pos_x = 0;
1606static int prt_pos_y = 0;
1607
1608 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001609mch_print_start_line(int margin, int page_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610{
1611 if (margin)
1612 prt_pos_x = -prt_number_width;
1613 else
1614 prt_pos_x = 0;
1615 prt_pos_y = page_line * prt_line_height
1616 + prt_tm.tmAscent + prt_tm.tmExternalLeading;
1617}
1618
1619 int
1620mch_print_text_out(char_u *p, int len)
1621{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001622 SIZE sz;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001623 WCHAR *wp;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001624 int wlen = len;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001625 int ret = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001626
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001627 wp = enc_to_utf16(p, &wlen);
1628 if (wp == NULL)
1629 return FALSE;
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001630
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001631 TextOutW(prt_dlg.hDC, prt_pos_x + prt_left_margin,
1632 prt_pos_y + prt_top_margin, wp, wlen);
1633 GetTextExtentPoint32W(prt_dlg.hDC, wp, wlen, &sz);
1634 vim_free(wp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001635 prt_pos_x += (sz.cx - prt_tm.tmOverhang);
1636 /* This is wrong when printing spaces for a TAB. */
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001637 if (p[len] != NUL)
1638 {
1639 wlen = MB_PTR2LEN(p + len);
1640 wp = enc_to_utf16(p + len, &wlen);
1641 if (wp != NULL)
1642 {
1643 GetTextExtentPoint32W(prt_dlg.hDC, wp, 1, &sz);
1644 ret = (prt_pos_x + prt_left_margin + sz.cx > prt_right_margin);
1645 vim_free(wp);
1646 }
1647 }
1648 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001649}
1650
1651 void
1652mch_print_set_font(int iBold, int iItalic, int iUnderline)
1653{
1654 SelectObject(prt_dlg.hDC, prt_font_handles[iBold][iItalic][iUnderline]);
1655}
1656
1657 void
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001658mch_print_set_bg(long_u bgcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001659{
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001660 SetBkColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC,
1661 swap_me((COLORREF)bgcol)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662 /*
1663 * With a white background we can draw characters transparent, which is
1664 * good for italic characters that overlap to the next char cell.
1665 */
1666 if (bgcol == 0xffffffUL)
1667 SetBkMode(prt_dlg.hDC, TRANSPARENT);
1668 else
1669 SetBkMode(prt_dlg.hDC, OPAQUE);
1670}
1671
1672 void
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001673mch_print_set_fg(long_u fgcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674{
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001675 SetTextColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC,
1676 swap_me((COLORREF)fgcol)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677}
1678
1679#endif /*FEAT_PRINTER && !FEAT_POSTSCRIPT*/
1680
Bram Moolenaar58d98232005-07-23 22:25:46 +00001681
1682
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683#if defined(FEAT_SHORTCUT) || defined(PROTO)
Bram Moolenaar82881492012-11-20 16:53:39 +01001684# ifndef PROTO
1685# include <shlobj.h>
1686# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687
Bram Moolenaardce1e892019-02-10 23:18:53 +01001688typedef enum _FILE_INFO_BY_HANDLE_CLASS_ {
1689 FileBasicInfo_,
1690 FileStandardInfo_,
1691 FileNameInfo_,
1692 FileRenameInfo_,
1693 FileDispositionInfo_,
1694 FileAllocationInfo_,
1695 FileEndOfFileInfo_,
1696 FileStreamInfo_,
1697 FileCompressionInfo_,
1698 FileAttributeTagInfo_,
1699 FileIdBothDirectoryInfo_,
1700 FileIdBothDirectoryRestartInfo_,
1701 FileIoPriorityHintInfo_,
1702 FileRemoteProtocolInfo_,
1703 FileFullDirectoryInfo_,
1704 FileFullDirectoryRestartInfo_,
1705 FileStorageInfo_,
1706 FileAlignmentInfo_,
1707 FileIdInfo_,
1708 FileIdExtdDirectoryInfo_,
1709 FileIdExtdDirectoryRestartInfo_,
1710 FileDispositionInfoEx_,
1711 FileRenameInfoEx_,
1712 MaximumFileInfoByHandleClass_
1713} FILE_INFO_BY_HANDLE_CLASS_;
1714
1715typedef struct _FILE_NAME_INFO_ {
1716 DWORD FileNameLength;
1717 WCHAR FileName[1];
1718} FILE_NAME_INFO_;
1719
1720typedef BOOL (WINAPI *pfnGetFileInformationByHandleEx)(
1721 HANDLE hFile,
1722 FILE_INFO_BY_HANDLE_CLASS_ FileInformationClass,
1723 LPVOID lpFileInformation,
1724 DWORD dwBufferSize);
1725static pfnGetFileInformationByHandleEx pGetFileInformationByHandleEx = NULL;
1726
1727typedef BOOL (WINAPI *pfnGetVolumeInformationByHandleW)(
1728 HANDLE hFile,
1729 LPWSTR lpVolumeNameBuffer,
1730 DWORD nVolumeNameSize,
1731 LPDWORD lpVolumeSerialNumber,
1732 LPDWORD lpMaximumComponentLength,
1733 LPDWORD lpFileSystemFlags,
1734 LPWSTR lpFileSystemNameBuffer,
1735 DWORD nFileSystemNameSize);
1736static pfnGetVolumeInformationByHandleW pGetVolumeInformationByHandleW = NULL;
1737
Bram Moolenaarb9cdb372019-04-17 18:24:35 +02001738 static char_u *
Bram Moolenaardce1e892019-02-10 23:18:53 +01001739resolve_reparse_point(char_u *fname)
1740{
1741 HANDLE h = INVALID_HANDLE_VALUE;
1742 DWORD size;
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001743 WCHAR *p;
Bram Moolenaardce1e892019-02-10 23:18:53 +01001744 char_u *rfname = NULL;
1745 FILE_NAME_INFO_ *nameinfo = NULL;
1746 WCHAR buff[MAX_PATH], *volnames = NULL;
1747 HANDLE hv;
1748 DWORD snfile, snfind;
1749 static BOOL loaded = FALSE;
1750
1751 if (pGetFileInformationByHandleEx == NULL ||
1752 pGetVolumeInformationByHandleW == NULL)
1753 {
1754 HMODULE hmod = GetModuleHandle("kernel32.dll");
1755
1756 if (loaded == TRUE)
1757 return NULL;
1758 pGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx)
1759 GetProcAddress(hmod, "GetFileInformationByHandleEx");
1760 pGetVolumeInformationByHandleW = (pfnGetVolumeInformationByHandleW)
1761 GetProcAddress(hmod, "GetVolumeInformationByHandleW");
1762 loaded = TRUE;
1763 if (pGetFileInformationByHandleEx == NULL ||
1764 pGetVolumeInformationByHandleW == NULL)
1765 return NULL;
1766 }
1767
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001768 p = enc_to_utf16(fname, NULL);
1769 if (p == NULL)
1770 goto fail;
1771
1772 if ((GetFileAttributesW(p) & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
Bram Moolenaardce1e892019-02-10 23:18:53 +01001773 {
Bram Moolenaardce1e892019-02-10 23:18:53 +01001774 vim_free(p);
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001775 goto fail;
Bram Moolenaardce1e892019-02-10 23:18:53 +01001776 }
Bram Moolenaardce1e892019-02-10 23:18:53 +01001777
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001778 h = CreateFileW(p, 0, 0, NULL, OPEN_EXISTING,
1779 FILE_FLAG_BACKUP_SEMANTICS, NULL);
1780 vim_free(p);
Bram Moolenaardce1e892019-02-10 23:18:53 +01001781
1782 if (h == INVALID_HANDLE_VALUE)
1783 goto fail;
1784
1785 size = sizeof(FILE_NAME_INFO_) + sizeof(WCHAR) * (MAX_PATH - 1);
1786 nameinfo = (FILE_NAME_INFO_*)alloc(size + sizeof(WCHAR));
1787 if (nameinfo == NULL)
1788 goto fail;
1789
1790 if (!pGetFileInformationByHandleEx(h, FileNameInfo_, nameinfo, size))
1791 goto fail;
1792
1793 nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = 0;
1794
1795 if (!pGetVolumeInformationByHandleW(
1796 h, NULL, 0, &snfile, NULL, NULL, NULL, 0))
1797 goto fail;
1798
1799 hv = FindFirstVolumeW(buff, MAX_PATH);
1800 if (hv == INVALID_HANDLE_VALUE)
1801 goto fail;
1802
1803 do {
1804 GetVolumeInformationW(
1805 buff, NULL, 0, &snfind, NULL, NULL, NULL, 0);
1806 if (snfind == snfile)
1807 break;
1808 } while (FindNextVolumeW(hv, buff, MAX_PATH));
1809
1810 FindVolumeClose(hv);
1811
1812 if (snfind != snfile)
1813 goto fail;
1814
1815 size = 0;
1816 if (!GetVolumePathNamesForVolumeNameW(buff, NULL, 0, &size) &&
1817 GetLastError() != ERROR_MORE_DATA)
1818 goto fail;
1819
1820 volnames = (WCHAR*)alloc(size * sizeof(WCHAR));
1821 if (!GetVolumePathNamesForVolumeNameW(buff, volnames, size,
1822 &size))
1823 goto fail;
1824
1825 wcscpy(buff, volnames);
1826 if (nameinfo->FileName[0] == '\\')
1827 wcscat(buff, nameinfo->FileName + 1);
1828 else
1829 wcscat(buff, nameinfo->FileName);
1830 rfname = utf16_to_enc(buff, NULL);
1831
1832fail:
1833 if (h != INVALID_HANDLE_VALUE)
1834 CloseHandle(h);
1835 if (nameinfo != NULL)
1836 vim_free(nameinfo);
1837 if (volnames != NULL)
1838 vim_free(volnames);
1839
1840 return rfname;
1841}
1842
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843/*
1844 * When "fname" is the name of a shortcut (*.lnk) resolve the file it points
1845 * to and return that name in allocated memory.
1846 * Otherwise NULL is returned.
1847 */
Bram Moolenaardce1e892019-02-10 23:18:53 +01001848 static char_u *
1849resolve_shortcut(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850{
1851 HRESULT hr;
1852 IShellLink *psl = NULL;
1853 IPersistFile *ppf = NULL;
1854 OLECHAR wsz[MAX_PATH];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001855 char_u *rfname = NULL;
1856 int len;
Bram Moolenaar604729e2013-08-30 16:44:19 +02001857 IShellLinkW *pslw = NULL;
1858 WIN32_FIND_DATAW ffdw; // we get those free of charge
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859
1860 /* Check if the file name ends in ".lnk". Avoid calling
1861 * CoCreateInstance(), it's quite slow. */
1862 if (fname == NULL)
1863 return rfname;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001864 len = (int)STRLEN(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0)
1866 return rfname;
1867
1868 CoInitialize(NULL);
1869
1870 // create a link manager object and request its interface
1871 hr = CoCreateInstance(
1872 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001873 &IID_IShellLinkW, (void**)&pslw);
1874 if (hr == S_OK)
1875 {
1876 WCHAR *p = enc_to_utf16(fname, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001878 if (p != NULL)
1879 {
1880 // Get a pointer to the IPersistFile interface.
1881 hr = pslw->lpVtbl->QueryInterface(
1882 pslw, &IID_IPersistFile, (void**)&ppf);
1883 if (hr != S_OK)
1884 goto shortcut_errorw;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001886 // "load" the name and resolve the link
1887 hr = ppf->lpVtbl->Load(ppf, p, STGM_READ);
1888 if (hr != S_OK)
1889 goto shortcut_errorw;
1890# if 0 // This makes Vim wait a long time if the target does not exist.
1891 hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI);
1892 if (hr != S_OK)
1893 goto shortcut_errorw;
Bram Moolenaar604729e2013-08-30 16:44:19 +02001894# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001896 // Get the path to the link target.
1897 ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR));
1898 hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0);
1899 if (hr == S_OK && wsz[0] != NUL)
1900 rfname = utf16_to_enc(wsz, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901
Bram Moolenaar0eb035c2019-04-02 22:15:55 +02001902shortcut_errorw:
1903 vim_free(p);
1904 }
1905 }
1906
Bram Moolenaar071d4272004-06-13 20:20:40 +00001907 // Release all interface pointers (both belong to the same object)
1908 if (ppf != NULL)
1909 ppf->lpVtbl->Release(ppf);
1910 if (psl != NULL)
1911 psl->lpVtbl->Release(psl);
Bram Moolenaar604729e2013-08-30 16:44:19 +02001912 if (pslw != NULL)
1913 pslw->lpVtbl->Release(pslw);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914
1915 CoUninitialize();
1916 return rfname;
1917}
Bram Moolenaardce1e892019-02-10 23:18:53 +01001918
1919 char_u *
1920mch_resolve_path(char_u *fname, int reparse_point)
1921{
1922 char_u *path = resolve_shortcut(fname);
1923
1924 if (path == NULL && reparse_point)
1925 path = resolve_reparse_point(fname);
1926 return path;
1927}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928#endif
1929
1930#if (defined(FEAT_EVAL) && !defined(FEAT_GUI)) || defined(PROTO)
1931/*
1932 * Bring ourselves to the foreground. Does work if the OS doesn't allow it.
1933 */
1934 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001935win32_set_foreground(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936{
1937# ifndef FEAT_GUI
1938 GetConsoleHwnd(); /* get value of s_hwnd */
1939# endif
1940 if (s_hwnd != 0)
1941 SetForegroundWindow(s_hwnd);
1942}
1943#endif
1944
1945#if defined(FEAT_CLIENTSERVER) || defined(PROTO)
1946/*
1947 * Client-server code for Vim
1948 *
1949 * Originally written by Paul Moore
1950 */
1951
1952/* In order to handle inter-process messages, we need to have a window. But
1953 * the functions in this module can be called before the main GUI window is
1954 * created (and may also be called in the console version, where there is no
1955 * GUI window at all).
1956 *
1957 * So we create a hidden window, and arrange to destroy it on exit.
1958 */
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02001959HWND message_window = 0; /* window that's handling messages */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960
1961#define VIM_CLASSNAME "VIM_MESSAGES"
1962#define VIM_CLASSNAME_LEN (sizeof(VIM_CLASSNAME) - 1)
1963
1964/* Communication is via WM_COPYDATA messages. The message type is send in
1965 * the dwData parameter. Types are defined here. */
1966#define COPYDATA_KEYS 0
1967#define COPYDATA_REPLY 1
1968#define COPYDATA_EXPR 10
1969#define COPYDATA_RESULT 11
1970#define COPYDATA_ERROR_RESULT 12
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001971#define COPYDATA_ENCODING 20
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972
1973/* This is a structure containing a server HWND and its name. */
1974struct server_id
1975{
1976 HWND hwnd;
1977 char_u *name;
1978};
1979
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001980/* Last received 'encoding' that the client uses. */
1981static char_u *client_enc = NULL;
1982
1983/*
1984 * Tell the other side what encoding we are using.
1985 * Errors are ignored.
1986 */
1987 static void
1988serverSendEnc(HWND target)
1989{
1990 COPYDATASTRUCT data;
1991
1992 data.dwData = COPYDATA_ENCODING;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001993 data.cbData = (DWORD)STRLEN(p_enc) + 1;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001994 data.lpData = p_enc;
1995 (void)SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
1996 (LPARAM)(&data));
1997}
1998
Bram Moolenaar071d4272004-06-13 20:20:40 +00001999/*
2000 * Clean up on exit. This destroys the hidden message window.
2001 */
2002 static void
2003#ifdef __BORLANDC__
2004 _RTLENTRYF
2005#endif
2006CleanUpMessaging(void)
2007{
2008 if (message_window != 0)
2009 {
2010 DestroyWindow(message_window);
2011 message_window = 0;
2012 }
2013}
2014
2015static int save_reply(HWND server, char_u *reply, int expr);
2016
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02002017/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018 * The window procedure for the hidden message window.
2019 * It handles callback messages and notifications from servers.
2020 * In order to process these messages, it is necessary to run a
2021 * message loop. Code which may run before the main message loop
2022 * is started (in the GUI) is careful to pump messages when it needs
2023 * to. Features which require message delivery during normal use will
2024 * not work in the console version - this basically means those
2025 * features which allow Vim to act as a server, rather than a client.
2026 */
2027 static LRESULT CALLBACK
2028Messaging_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2029{
2030 if (msg == WM_COPYDATA)
2031 {
2032 /* This is a message from another Vim. The dwData member of the
2033 * COPYDATASTRUCT determines the type of message:
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002034 * COPYDATA_ENCODING:
2035 * The encoding that the client uses. Following messages will
2036 * use this encoding, convert if needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037 * COPYDATA_KEYS:
2038 * A key sequence. We are a server, and a client wants these keys
2039 * adding to the input queue.
2040 * COPYDATA_REPLY:
2041 * A reply. We are a client, and a server has sent this message
2042 * in response to a request. (server2client())
2043 * COPYDATA_EXPR:
2044 * An expression. We are a server, and a client wants us to
2045 * evaluate this expression.
2046 * COPYDATA_RESULT:
2047 * A reply. We are a client, and a server has sent this message
2048 * in response to a COPYDATA_EXPR.
2049 * COPYDATA_ERROR_RESULT:
2050 * A reply. We are a client, and a server has sent this message
2051 * in response to a COPYDATA_EXPR that failed to evaluate.
2052 */
2053 COPYDATASTRUCT *data = (COPYDATASTRUCT*)lParam;
2054 HWND sender = (HWND)wParam;
2055 COPYDATASTRUCT reply;
2056 char_u *res;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002057 int retval;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002058 char_u *str;
2059 char_u *tofree;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060
2061 switch (data->dwData)
2062 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002063 case COPYDATA_ENCODING:
2064 /* Remember the encoding that the client uses. */
2065 vim_free(client_enc);
2066 client_enc = enc_canonize((char_u *)data->lpData);
2067 return 1;
2068
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 case COPYDATA_KEYS:
2070 /* Remember who sent this, for <client> */
2071 clientWindow = sender;
2072
2073 /* Add the received keys to the input buffer. The loop waiting
2074 * for the user to do something should check the input buffer. */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002075 str = serverConvert(client_enc, (char_u *)data->lpData, &tofree);
2076 server_to_input_buf(str);
2077 vim_free(tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078
2079# ifdef FEAT_GUI
2080 /* Wake up the main GUI loop. */
2081 if (s_hwnd != 0)
2082 PostMessage(s_hwnd, WM_NULL, 0, 0);
2083# endif
2084 return 1;
2085
2086 case COPYDATA_EXPR:
2087 /* Remember who sent this, for <client> */
2088 clientWindow = sender;
2089
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002090 str = serverConvert(client_enc, (char_u *)data->lpData, &tofree);
2091 res = eval_client_expr_to_string(str);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002092
Bram Moolenaar071d4272004-06-13 20:20:40 +00002093 if (res == NULL)
2094 {
Bram Moolenaar15bf76d2017-03-18 16:18:37 +01002095 char *err = _(e_invexprmsg);
2096 size_t len = STRLEN(str) + STRLEN(err) + 5;
2097
Bram Moolenaar1662ce12017-03-19 21:47:50 +01002098 res = alloc((unsigned)len);
Bram Moolenaar15bf76d2017-03-18 16:18:37 +01002099 if (res != NULL)
2100 vim_snprintf((char *)res, len, "%s: \"%s\"", err, str);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101 reply.dwData = COPYDATA_ERROR_RESULT;
2102 }
2103 else
2104 reply.dwData = COPYDATA_RESULT;
2105 reply.lpData = res;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002106 reply.cbData = (DWORD)STRLEN(res) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002107
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002108 serverSendEnc(sender);
Bram Moolenaar5246cd72013-06-16 16:41:47 +02002109 retval = (int)SendMessage(sender, WM_COPYDATA,
2110 (WPARAM)message_window, (LPARAM)(&reply));
Bram Moolenaar15bf76d2017-03-18 16:18:37 +01002111 vim_free(tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 vim_free(res);
2113 return retval;
2114
2115 case COPYDATA_REPLY:
2116 case COPYDATA_RESULT:
2117 case COPYDATA_ERROR_RESULT:
2118 if (data->lpData != NULL)
2119 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002120 str = serverConvert(client_enc, (char_u *)data->lpData,
2121 &tofree);
2122 if (tofree == NULL)
2123 str = vim_strsave(str);
2124 if (save_reply(sender, str,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125 (data->dwData == COPYDATA_REPLY ? 0 :
2126 (data->dwData == COPYDATA_RESULT ? 1 :
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002127 2))) == FAIL)
2128 vim_free(str);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002129 else if (data->dwData == COPYDATA_REPLY)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002130 {
Bram Moolenaar427d51c2013-06-16 16:01:25 +02002131 char_u winstr[30];
2132
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002133 sprintf((char *)winstr, PRINTF_HEX_LONG_U, (long_u)sender);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002134 apply_autocmds(EVENT_REMOTEREPLY, winstr, str,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002135 TRUE, curbuf);
2136 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002137 }
2138 return 1;
2139 }
2140
2141 return 0;
2142 }
2143
2144 else if (msg == WM_ACTIVATE && wParam == WA_ACTIVE)
2145 {
2146 /* When the message window is activated (brought to the foreground),
2147 * this actually applies to the text window. */
2148#ifndef FEAT_GUI
2149 GetConsoleHwnd(); /* get value of s_hwnd */
2150#endif
2151 if (s_hwnd != 0)
2152 {
2153 SetForegroundWindow(s_hwnd);
2154 return 0;
2155 }
2156 }
2157
2158 return DefWindowProc(hwnd, msg, wParam, lParam);
2159}
2160
2161/*
2162 * Initialise the message handling process. This involves creating a window
2163 * to handle messages - the window will not be visible.
2164 */
2165 void
2166serverInitMessaging(void)
2167{
2168 WNDCLASS wndclass;
2169 HINSTANCE s_hinst;
2170
2171 /* Clean up on exit */
2172 atexit(CleanUpMessaging);
2173
2174 /* Register a window class - we only really care
2175 * about the window procedure
2176 */
2177 s_hinst = (HINSTANCE)GetModuleHandle(0);
2178 wndclass.style = 0;
2179 wndclass.lpfnWndProc = Messaging_WndProc;
2180 wndclass.cbClsExtra = 0;
2181 wndclass.cbWndExtra = 0;
2182 wndclass.hInstance = s_hinst;
2183 wndclass.hIcon = NULL;
2184 wndclass.hCursor = NULL;
2185 wndclass.hbrBackground = NULL;
2186 wndclass.lpszMenuName = NULL;
2187 wndclass.lpszClassName = VIM_CLASSNAME;
2188 RegisterClass(&wndclass);
2189
2190 /* Create the message window. It will be hidden, so the details don't
2191 * matter. Don't use WS_OVERLAPPEDWINDOW, it will make a shortcut remove
2192 * focus from gvim. */
2193 message_window = CreateWindow(VIM_CLASSNAME, "",
2194 WS_POPUPWINDOW | WS_CAPTION,
2195 CW_USEDEFAULT, CW_USEDEFAULT,
2196 100, 100, NULL, NULL,
2197 s_hinst, NULL);
2198}
2199
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002200/* Used by serverSendToVim() to find an alternate server name. */
2201static char_u *altname_buf_ptr = NULL;
2202
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203/*
2204 * Get the title of the window "hwnd", which is the Vim server name, in
2205 * "name[namelen]" and return the length.
2206 * Returns zero if window "hwnd" is not a Vim server.
2207 */
2208 static int
2209getVimServerName(HWND hwnd, char *name, int namelen)
2210{
2211 int len;
2212 char buffer[VIM_CLASSNAME_LEN + 1];
2213
2214 /* Ignore windows which aren't Vim message windows */
2215 len = GetClassName(hwnd, buffer, sizeof(buffer));
2216 if (len != VIM_CLASSNAME_LEN || STRCMP(buffer, VIM_CLASSNAME) != 0)
2217 return 0;
2218
2219 /* Get the title of the window */
2220 return GetWindowText(hwnd, name, namelen);
2221}
2222
2223 static BOOL CALLBACK
2224enumWindowsGetServer(HWND hwnd, LPARAM lparam)
2225{
2226 struct server_id *id = (struct server_id *)lparam;
2227 char server[MAX_PATH];
2228
2229 /* Get the title of the window */
2230 if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2231 return TRUE;
2232
2233 /* If this is the server we're looking for, return its HWND */
2234 if (STRICMP(server, id->name) == 0)
2235 {
2236 id->hwnd = hwnd;
2237 return FALSE;
2238 }
2239
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002240 /* If we are looking for an alternate server, remember this name. */
2241 if (altname_buf_ptr != NULL
2242 && STRNICMP(server, id->name, STRLEN(id->name)) == 0
2243 && vim_isdigit(server[STRLEN(id->name)]))
2244 {
2245 STRCPY(altname_buf_ptr, server);
2246 altname_buf_ptr = NULL; /* don't use another name */
2247 }
2248
Bram Moolenaar071d4272004-06-13 20:20:40 +00002249 /* Otherwise, keep looking */
2250 return TRUE;
2251}
2252
2253 static BOOL CALLBACK
2254enumWindowsGetNames(HWND hwnd, LPARAM lparam)
2255{
2256 garray_T *ga = (garray_T *)lparam;
2257 char server[MAX_PATH];
2258
2259 /* Get the title of the window */
2260 if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2261 return TRUE;
2262
2263 /* Add the name to the list */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002264 ga_concat(ga, (char_u *)server);
2265 ga_concat(ga, (char_u *)"\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266 return TRUE;
2267}
2268
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002269struct enum_windows_s
2270{
2271 WNDENUMPROC lpEnumFunc;
2272 LPARAM lParam;
2273};
2274
2275 static BOOL CALLBACK
2276enum_windows_child(HWND hwnd, LPARAM lParam)
2277{
2278 struct enum_windows_s *ew = (struct enum_windows_s *)lParam;
2279
2280 return (ew->lpEnumFunc)(hwnd, ew->lParam);
2281}
2282
2283 static BOOL CALLBACK
2284enum_windows_toplevel(HWND hwnd, LPARAM lParam)
2285{
2286 struct enum_windows_s *ew = (struct enum_windows_s *)lParam;
2287
Bram Moolenaar95ba5c32018-10-07 22:47:07 +02002288 if ((ew->lpEnumFunc)(hwnd, ew->lParam))
2289 return TRUE;
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002290 return EnumChildWindows(hwnd, enum_windows_child, lParam);
2291}
2292
2293/* Enumerate all windows including children. */
2294 static BOOL
2295enum_windows(WNDENUMPROC lpEnumFunc, LPARAM lParam)
2296{
2297 struct enum_windows_s ew;
2298
2299 ew.lpEnumFunc = lpEnumFunc;
2300 ew.lParam = lParam;
2301 return EnumWindows(enum_windows_toplevel, (LPARAM)&ew);
2302}
2303
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 static HWND
2305findServer(char_u *name)
2306{
2307 struct server_id id;
2308
2309 id.name = name;
2310 id.hwnd = 0;
2311
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002312 enum_windows(enumWindowsGetServer, (LPARAM)(&id));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002313
2314 return id.hwnd;
2315}
2316
2317 void
2318serverSetName(char_u *name)
2319{
2320 char_u *ok_name;
2321 HWND hwnd = 0;
2322 int i = 0;
2323 char_u *p;
2324
2325 /* Leave enough space for a 9-digit suffix to ensure uniqueness! */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002326 ok_name = alloc((unsigned)STRLEN(name) + 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327
2328 STRCPY(ok_name, name);
2329 p = ok_name + STRLEN(name);
2330
2331 for (;;)
2332 {
2333 /* This is inefficient - we're doing an EnumWindows loop for each
2334 * possible name. It would be better to grab all names in one go,
2335 * and scan the list each time...
2336 */
2337 hwnd = findServer(ok_name);
2338 if (hwnd == 0)
2339 break;
2340
2341 ++i;
2342 if (i >= 1000)
2343 break;
2344
2345 sprintf((char *)p, "%d", i);
2346 }
2347
2348 if (hwnd != 0)
2349 vim_free(ok_name);
2350 else
2351 {
2352 /* Remember the name */
2353 serverName = ok_name;
2354#ifdef FEAT_TITLE
2355 need_maketitle = TRUE; /* update Vim window title later */
2356#endif
2357
2358 /* Update the message window title */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01002359 SetWindowText(message_window, (LPCSTR)ok_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360
2361#ifdef FEAT_EVAL
2362 /* Set the servername variable */
2363 set_vim_var_string(VV_SEND_SERVER, serverName, -1);
2364#endif
2365 }
2366}
2367
2368 char_u *
2369serverGetVimNames(void)
2370{
2371 garray_T ga;
2372
2373 ga_init2(&ga, 1, 100);
2374
Bram Moolenaarc0543e12018-10-07 20:35:12 +02002375 enum_windows(enumWindowsGetNames, (LPARAM)(&ga));
Bram Moolenaar269ec652004-07-29 08:43:53 +00002376 ga_append(&ga, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377
2378 return ga.ga_data;
2379}
2380
2381 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002382serverSendReply(
2383 char_u *name, /* Where to send. */
2384 char_u *reply) /* What to send. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385{
2386 HWND target;
2387 COPYDATASTRUCT data;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002388 long_u n = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389
2390 /* The "name" argument is a magic cookie obtained from expand("<client>").
2391 * It should be of the form 0xXXXXX - i.e. a C hex literal, which is the
2392 * value of the client's message window HWND.
2393 */
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002394 sscanf((char *)name, SCANF_HEX_LONG_U, &n);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395 if (n == 0)
2396 return -1;
2397
2398 target = (HWND)n;
2399 if (!IsWindow(target))
2400 return -1;
2401
2402 data.dwData = COPYDATA_REPLY;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002403 data.cbData = (DWORD)STRLEN(reply) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 data.lpData = reply;
2405
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002406 serverSendEnc(target);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002407 if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
2408 (LPARAM)(&data)))
2409 return 0;
2410
2411 return -1;
2412}
2413
2414 int
Bram Moolenaar05540972016-01-30 20:31:25 +01002415serverSendToVim(
2416 char_u *name, /* Where to send. */
2417 char_u *cmd, /* What to send. */
2418 char_u **result, /* Result of eval'ed expression */
2419 void *ptarget, /* HWND of server */
2420 int asExpr, /* Expression or keys? */
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002421 int timeout, /* timeout in seconds or zero */
Bram Moolenaar05540972016-01-30 20:31:25 +01002422 int silent) /* don't complain about no server */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423{
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002424 HWND target;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 COPYDATASTRUCT data;
2426 char_u *retval = NULL;
2427 int retcode = 0;
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002428 char_u altname_buf[MAX_PATH];
2429
Bram Moolenaar7416f3e2017-03-18 18:10:13 +01002430 /* Execute locally if no display or target is ourselves */
2431 if (serverName != NULL && STRICMP(name, serverName) == 0)
2432 return sendToLocalVim(cmd, asExpr, result);
2433
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002434 /* If the server name does not end in a digit then we look for an
2435 * alternate name. e.g. when "name" is GVIM the we may find GVIM2. */
2436 if (STRLEN(name) > 1 && !vim_isdigit(name[STRLEN(name) - 1]))
2437 altname_buf_ptr = altname_buf;
2438 altname_buf[0] = NUL;
2439 target = findServer(name);
2440 altname_buf_ptr = NULL;
2441 if (target == 0 && altname_buf[0] != NUL)
2442 /* Use another server name we found. */
2443 target = findServer(altname_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444
2445 if (target == 0)
2446 {
2447 if (!silent)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002448 semsg(_(e_noserver), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002449 return -1;
2450 }
2451
2452 if (ptarget)
2453 *(HWND *)ptarget = target;
2454
2455 data.dwData = asExpr ? COPYDATA_EXPR : COPYDATA_KEYS;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002456 data.cbData = (DWORD)STRLEN(cmd) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 data.lpData = cmd;
2458
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002459 serverSendEnc(target);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460 if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
2461 (LPARAM)(&data)) == 0)
2462 return -1;
2463
2464 if (asExpr)
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002465 retval = serverGetReply(target, &retcode, TRUE, TRUE, timeout);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002466
2467 if (result == NULL)
2468 vim_free(retval);
2469 else
2470 *result = retval; /* Caller assumes responsibility for freeing */
2471
2472 return retcode;
2473}
2474
2475/*
2476 * Bring the server to the foreground.
2477 */
2478 void
Bram Moolenaar05540972016-01-30 20:31:25 +01002479serverForeground(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002480{
2481 HWND target = findServer(name);
2482
2483 if (target != 0)
2484 SetForegroundWindow(target);
2485}
2486
2487/* Replies from server need to be stored until the client picks them up via
2488 * remote_read(). So we maintain a list of server-id/reply pairs.
2489 * Note that there could be multiple replies from one server pending if the
2490 * client is slow picking them up.
2491 * We just store the replies in a simple list. When we remove an entry, we
2492 * move list entries down to fill the gap.
2493 * The server ID is simply the HWND.
2494 */
2495typedef struct
2496{
2497 HWND server; /* server window */
2498 char_u *reply; /* reply string */
2499 int expr_result; /* 0 for REPLY, 1 for RESULT 2 for error */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002500} reply_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002501
2502static garray_T reply_list = {0, 0, sizeof(reply_T), 5, 0};
2503
2504#define REPLY_ITEM(i) ((reply_T *)(reply_list.ga_data) + (i))
2505#define REPLY_COUNT (reply_list.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506
2507/* Flag which is used to wait for a reply */
2508static int reply_received = 0;
2509
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002510/*
2511 * Store a reply. "reply" must be allocated memory (or NULL).
2512 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513 static int
2514save_reply(HWND server, char_u *reply, int expr)
2515{
2516 reply_T *rep;
2517
2518 if (ga_grow(&reply_list, 1) == FAIL)
2519 return FAIL;
2520
2521 rep = REPLY_ITEM(REPLY_COUNT);
2522 rep->server = server;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002523 rep->reply = reply;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524 rep->expr_result = expr;
2525 if (rep->reply == NULL)
2526 return FAIL;
2527
2528 ++REPLY_COUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002529 reply_received = 1;
2530 return OK;
2531}
2532
2533/*
2534 * Get a reply from server "server".
2535 * When "expr_res" is non NULL, get the result of an expression, otherwise a
2536 * server2client() message.
2537 * When non NULL, point to return code. 0 => OK, -1 => ERROR
2538 * If "remove" is TRUE, consume the message, the caller must free it then.
2539 * if "wait" is TRUE block until a message arrives (or the server exits).
2540 */
2541 char_u *
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002542serverGetReply(HWND server, int *expr_res, int remove, int wait, int timeout)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543{
2544 int i;
2545 char_u *reply;
2546 reply_T *rep;
Bram Moolenaar15e737f2017-03-18 21:22:47 +01002547 int did_process = FALSE;
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002548 time_t start;
2549 time_t now;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550
2551 /* When waiting, loop until the message waiting for is received. */
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002552 time(&start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553 for (;;)
2554 {
2555 /* Reset this here, in case a message arrives while we are going
2556 * through the already received messages. */
2557 reply_received = 0;
2558
2559 for (i = 0; i < REPLY_COUNT; ++i)
2560 {
2561 rep = REPLY_ITEM(i);
2562 if (rep->server == server
2563 && ((rep->expr_result != 0) == (expr_res != NULL)))
2564 {
2565 /* Save the values we've found for later */
2566 reply = rep->reply;
2567 if (expr_res != NULL)
2568 *expr_res = rep->expr_result == 1 ? 0 : -1;
2569
2570 if (remove)
2571 {
2572 /* Move the rest of the list down to fill the gap */
2573 mch_memmove(rep, rep + 1,
2574 (REPLY_COUNT - i - 1) * sizeof(reply_T));
2575 --REPLY_COUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002576 }
2577
2578 /* Return the reply to the caller, who takes on responsibility
2579 * for freeing it if "remove" is TRUE. */
2580 return reply;
2581 }
2582 }
2583
2584 /* If we got here, we didn't find a reply. Return immediately if the
2585 * "wait" parameter isn't set. */
2586 if (!wait)
Bram Moolenaar15e737f2017-03-18 21:22:47 +01002587 {
2588 /* Process pending messages once. Without this, looping on
2589 * remote_peek() would never get the reply. */
2590 if (!did_process)
2591 {
2592 did_process = TRUE;
2593 serverProcessPendingMessages();
2594 continue;
2595 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596 break;
Bram Moolenaar15e737f2017-03-18 21:22:47 +01002597 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598
2599 /* We need to wait for a reply. Enter a message loop until the
2600 * "reply_received" flag gets set. */
2601
2602 /* Loop until we receive a reply */
2603 while (reply_received == 0)
2604 {
Bram Moolenaar42205552017-03-18 19:42:22 +01002605#ifdef FEAT_TIMERS
Bram Moolenaard23a8232018-02-10 18:45:26 +01002606 /* TODO: use the return value to decide how long to wait. */
Bram Moolenaar42205552017-03-18 19:42:22 +01002607 check_due_timer();
2608#endif
Bram Moolenaar81b9d0b2017-03-19 21:20:53 +01002609 time(&now);
2610 if (timeout > 0 && (now - start) >= timeout)
2611 break;
2612
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613 /* Wait for a SendMessage() call to us. This could be the reply
2614 * we are waiting for. Use a timeout of a second, to catch the
2615 * situation that the server died unexpectedly. */
2616 MsgWaitForMultipleObjects(0, NULL, TRUE, 1000, QS_ALLINPUT);
2617
2618 /* If the server has died, give up */
2619 if (!IsWindow(server))
2620 return NULL;
2621
2622 serverProcessPendingMessages();
2623 }
2624 }
2625
2626 return NULL;
2627}
2628
2629/*
2630 * Process any messages in the Windows message queue.
2631 */
2632 void
2633serverProcessPendingMessages(void)
2634{
2635 MSG msg;
2636
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02002637 while (pPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002638 {
2639 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02002640 pDispatchMessage(&msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 }
2642}
2643
2644#endif /* FEAT_CLIENTSERVER */
2645
2646#if defined(FEAT_GUI) || (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) \
2647 || defined(PROTO)
2648
2649struct charset_pair
2650{
2651 char *name;
2652 BYTE charset;
2653};
2654
2655static struct charset_pair
2656charset_pairs[] =
2657{
2658 {"ANSI", ANSI_CHARSET},
2659 {"CHINESEBIG5", CHINESEBIG5_CHARSET},
2660 {"DEFAULT", DEFAULT_CHARSET},
2661 {"HANGEUL", HANGEUL_CHARSET},
2662 {"OEM", OEM_CHARSET},
2663 {"SHIFTJIS", SHIFTJIS_CHARSET},
2664 {"SYMBOL", SYMBOL_CHARSET},
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 {"ARABIC", ARABIC_CHARSET},
2666 {"BALTIC", BALTIC_CHARSET},
2667 {"EASTEUROPE", EASTEUROPE_CHARSET},
2668 {"GB2312", GB2312_CHARSET},
2669 {"GREEK", GREEK_CHARSET},
2670 {"HEBREW", HEBREW_CHARSET},
2671 {"JOHAB", JOHAB_CHARSET},
2672 {"MAC", MAC_CHARSET},
2673 {"RUSSIAN", RUSSIAN_CHARSET},
2674 {"THAI", THAI_CHARSET},
2675 {"TURKISH", TURKISH_CHARSET},
Bram Moolenaarcea912a2016-10-12 14:20:24 +02002676#ifdef VIETNAMESE_CHARSET
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 {"VIETNAMESE", VIETNAMESE_CHARSET},
Bram Moolenaar071d4272004-06-13 20:20:40 +00002678#endif
2679 {NULL, 0}
2680};
2681
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002682struct quality_pair
2683{
2684 char *name;
2685 DWORD quality;
2686};
2687
2688static struct quality_pair
2689quality_pairs[] = {
2690#ifdef CLEARTYPE_QUALITY
2691 {"CLEARTYPE", CLEARTYPE_QUALITY},
2692#endif
2693#ifdef ANTIALIASED_QUALITY
2694 {"ANTIALIASED", ANTIALIASED_QUALITY},
2695#endif
Bram Moolenaar73a733e2016-05-11 21:05:05 +02002696#ifdef NONANTIALIASED_QUALITY
2697 {"NONANTIALIASED", NONANTIALIASED_QUALITY},
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002698#endif
2699#ifdef PROOF_QUALITY
2700 {"PROOF", PROOF_QUALITY},
2701#endif
Bram Moolenaar4c9ce052016-04-04 21:06:19 +02002702#ifdef DRAFT_QUALITY
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002703 {"DRAFT", DRAFT_QUALITY},
2704#endif
2705 {"DEFAULT", DEFAULT_QUALITY},
2706 {NULL, 0}
2707};
2708
Bram Moolenaar071d4272004-06-13 20:20:40 +00002709/*
2710 * Convert a charset ID to a name.
2711 * Return NULL when not recognized.
2712 */
2713 char *
2714charset_id2name(int id)
2715{
2716 struct charset_pair *cp;
2717
2718 for (cp = charset_pairs; cp->name != NULL; ++cp)
2719 if ((BYTE)id == cp->charset)
2720 break;
2721 return cp->name;
2722}
2723
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02002724/*
2725 * Convert a quality ID to a name.
2726 * Return NULL when not recognized.
2727 */
2728 char *
2729quality_id2name(DWORD id)
2730{
2731 struct quality_pair *qp;
2732
2733 for (qp = quality_pairs; qp->name != NULL; ++qp)
2734 if (id == qp->quality)
2735 break;
2736 return qp->name;
2737}
2738
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002739static const LOGFONTW s_lfDefault =
Bram Moolenaar071d4272004-06-13 20:20:40 +00002740{
2741 -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2742 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2743 PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE,
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002744 L"Fixedsys" /* see _ReadVimIni */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745};
2746
2747/* Initialise the "current height" to -12 (same as s_lfDefault) just
2748 * in case the user specifies a font in "guifont" with no size before a font
2749 * with an explicit size has been set. This defaults the size to this value
2750 * (-12 equates to roughly 9pt).
2751 */
2752int current_font_height = -12; /* also used in gui_w48.c */
2753
2754/* Convert a string representing a point size into pixels. The string should
2755 * be a positive decimal number, with an optional decimal point (eg, "12", or
2756 * "10.5"). The pixel value is returned, and a pointer to the next unconverted
2757 * character is stored in *end. The flag "vertical" says whether this
2758 * calculation is for a vertical (height) size or a horizontal (width) one.
2759 */
2760 static int
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002761points_to_pixels(WCHAR *str, WCHAR **end, int vertical, long_i pprinter_dc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002762{
2763 int pixels;
2764 int points = 0;
2765 int divisor = 0;
2766 HWND hwnd = (HWND)0;
2767 HDC hdc;
2768 HDC printer_dc = (HDC)pprinter_dc;
2769
2770 while (*str != NUL)
2771 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002772 if (*str == L'.' && divisor == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002773 {
2774 /* Start keeping a divisor, for later */
2775 divisor = 1;
2776 }
2777 else
2778 {
2779 if (!VIM_ISDIGIT(*str))
2780 break;
2781
2782 points *= 10;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002783 points += *str - L'0';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002784 divisor *= 10;
2785 }
2786 ++str;
2787 }
2788
2789 if (divisor == 0)
2790 divisor = 1;
2791
2792 if (printer_dc == NULL)
2793 {
2794 hwnd = GetDesktopWindow();
2795 hdc = GetWindowDC(hwnd);
2796 }
2797 else
2798 hdc = printer_dc;
2799
2800 pixels = MulDiv(points,
2801 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX),
2802 72 * divisor);
2803
2804 if (printer_dc == NULL)
2805 ReleaseDC(hwnd, hdc);
2806
2807 *end = str;
2808 return pixels;
2809}
2810
2811 static int CALLBACK
2812font_enumproc(
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002813 ENUMLOGFONTW *elf,
2814 NEWTEXTMETRICW *ntm UNUSED,
2815 DWORD type UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002816 LPARAM lparam)
2817{
2818 /* Return value:
2819 * 0 = terminate now (monospace & ANSI)
2820 * 1 = continue, still no luck...
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002821 * 2 = continue, but we have an acceptable LOGFONTW
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 * (monospace, not ANSI)
2823 * We use these values, as EnumFontFamilies returns 1 if the
2824 * callback function is never called. So, we check the return as
2825 * 0 = perfect, 2 = OK, 1 = no good...
2826 * It's not pretty, but it works!
2827 */
2828
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002829 LOGFONTW *lf = (LOGFONTW *)(lparam);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830
2831#ifndef FEAT_PROPORTIONAL_FONTS
2832 /* Ignore non-monospace fonts without further ado */
2833 if ((ntm->tmPitchAndFamily & 1) != 0)
2834 return 1;
2835#endif
2836
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002837 /* Remember this LOGFONTW as a "possible" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838 *lf = elf->elfLogFont;
2839
2840 /* Terminate the scan as soon as we find an ANSI font */
2841 if (lf->lfCharSet == ANSI_CHARSET
2842 || lf->lfCharSet == OEM_CHARSET
2843 || lf->lfCharSet == DEFAULT_CHARSET)
2844 return 0;
2845
2846 /* Continue the scan - we have a non-ANSI font */
2847 return 2;
2848}
2849
2850 static int
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002851init_logfont(LOGFONTW *lf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852{
2853 int n;
2854 HWND hwnd = GetDesktopWindow();
2855 HDC hdc = GetWindowDC(hwnd);
2856
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002857 n = EnumFontFamiliesW(hdc,
2858 lf->lfFaceName,
2859 (FONTENUMPROCW)font_enumproc,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860 (LPARAM)lf);
2861
2862 ReleaseDC(hwnd, hdc);
2863
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02002864 /* If we couldn't find a usable font, return failure */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 if (n == 1)
2866 return FAIL;
2867
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002868 /* Tidy up the rest of the LOGFONTW structure. We set to a basic
Bram Moolenaar071d4272004-06-13 20:20:40 +00002869 * font - get_logfont() sets bold, italic, etc based on the user's
2870 * input.
2871 */
2872 lf->lfHeight = current_font_height;
2873 lf->lfWidth = 0;
2874 lf->lfItalic = FALSE;
2875 lf->lfUnderline = FALSE;
2876 lf->lfStrikeOut = FALSE;
2877 lf->lfWeight = FW_NORMAL;
2878
2879 /* Return success */
2880 return OK;
2881}
2882
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002883/*
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002884 * Compare a UTF-16 string and an ASCII string literally.
2885 * Only works all the code points are inside ASCII range.
2886 */
2887 static int
2888utf16ascncmp(const WCHAR *w, const char *p, size_t n)
2889{
2890 size_t i;
2891
2892 for (i = 0; i < n; i++)
2893 {
2894 if (w[i] == 0 || w[i] != p[i])
2895 return w[i] - p[i];
2896 }
2897 return 0;
2898}
2899
2900/*
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002901 * Get font info from "name" into logfont "lf".
2902 * Return OK for a valid name, FAIL otherwise.
2903 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 int
2905get_logfont(
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002906 LOGFONTW *lf,
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002907 char_u *name,
2908 HDC printer_dc,
2909 int verbose)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002910{
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002911 WCHAR *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002912 int i;
Bram Moolenaarb1692e22014-03-12 19:24:37 +01002913 int ret = FAIL;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002914 static LOGFONTW *lastlf = NULL;
2915 WCHAR *wname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916
2917 *lf = s_lfDefault;
2918 if (name == NULL)
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002919 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002920
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002921 wname = enc_to_utf16(name, NULL);
2922 if (wname == NULL)
2923 return FAIL;
2924
2925 if (wcscmp(wname, L"*") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01002927#if defined(FEAT_GUI_MSWIN)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002928 CHOOSEFONTW cf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002929 /* if name is "*", bring up std font dialog: */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02002930 vim_memset(&cf, 0, sizeof(cf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002931 cf.lStructSize = sizeof(cf);
2932 cf.hwndOwner = s_hwnd;
2933 cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY | CF_INITTOLOGFONTSTRUCT;
2934 if (lastlf != NULL)
2935 *lf = *lastlf;
2936 cf.lpLogFont = lf;
2937 cf.nFontType = 0 ; //REGULAR_FONTTYPE;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002938 if (ChooseFontW(&cf))
Bram Moolenaarb1692e22014-03-12 19:24:37 +01002939 ret = OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940#endif
Bram Moolenaarb1692e22014-03-12 19:24:37 +01002941 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942 }
2943
2944 /*
2945 * Split name up, it could be <name>:h<height>:w<width> etc.
2946 */
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002947 for (p = wname; *p && *p != L':'; p++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002948 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002949 if (p - wname + 1 >= LF_FACESIZE)
Bram Moolenaarb1692e22014-03-12 19:24:37 +01002950 goto theend; /* Name too long */
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002951 lf->lfFaceName[p - wname] = *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002952 }
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002953 if (p != wname)
2954 lf->lfFaceName[p - wname] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955
2956 /* First set defaults */
2957 lf->lfHeight = -12;
2958 lf->lfWidth = 0;
2959 lf->lfWeight = FW_NORMAL;
2960 lf->lfItalic = FALSE;
2961 lf->lfUnderline = FALSE;
2962 lf->lfStrikeOut = FALSE;
2963
2964 /*
2965 * If the font can't be found, try replacing '_' by ' '.
2966 */
2967 if (init_logfont(lf) == FAIL)
2968 {
2969 int did_replace = FALSE;
2970
2971 for (i = 0; lf->lfFaceName[i]; ++i)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002972 if (lf->lfFaceName[i] == L'_')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002973 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002974 lf->lfFaceName[i] = L' ';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002975 did_replace = TRUE;
2976 }
2977 if (!did_replace || init_logfont(lf) == FAIL)
Bram Moolenaarb1692e22014-03-12 19:24:37 +01002978 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979 }
2980
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002981 while (*p == L':')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002982 p++;
2983
2984 /* Set the values found after ':' */
2985 while (*p)
2986 {
2987 switch (*p++)
2988 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002989 case L'h':
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002990 lf->lfHeight = - points_to_pixels(p, &p, TRUE, (long_i)printer_dc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002992 case L'w':
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002993 lf->lfWidth = points_to_pixels(p, &p, FALSE, (long_i)printer_dc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 break;
Bram Moolenaarf720d0a2019-04-28 14:02:47 +02002995 case L'W':
2996 lf->lfWeight = wcstol(p, &p, 10);
2997 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01002998 case L'b':
Bram Moolenaar071d4272004-06-13 20:20:40 +00002999 lf->lfWeight = FW_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003000 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003001 case L'i':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002 lf->lfItalic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003003 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003004 case L'u':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005 lf->lfUnderline = TRUE;
3006 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003007 case L's':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003008 lf->lfStrikeOut = TRUE;
3009 break;
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003010 case L'c':
Bram Moolenaar071d4272004-06-13 20:20:40 +00003011 {
3012 struct charset_pair *cp;
3013
3014 for (cp = charset_pairs; cp->name != NULL; ++cp)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003015 if (utf16ascncmp(p, cp->name, strlen(cp->name)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 {
3017 lf->lfCharSet = cp->charset;
3018 p += strlen(cp->name);
3019 break;
3020 }
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00003021 if (cp->name == NULL && verbose)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003023 char_u *s = utf16_to_enc(p, NULL);
3024 semsg(_("E244: Illegal charset name \"%s\" in font name \"%s\""), s, name);
3025 vim_free(s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026 break;
3027 }
3028 break;
3029 }
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003030 case L'q':
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003031 {
3032 struct quality_pair *qp;
3033
3034 for (qp = quality_pairs; qp->name != NULL; ++qp)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003035 if (utf16ascncmp(p, qp->name, strlen(qp->name)) == 0)
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003036 {
3037 lf->lfQuality = qp->quality;
3038 p += strlen(qp->name);
3039 break;
3040 }
3041 if (qp->name == NULL && verbose)
3042 {
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003043 char_u *s = utf16_to_enc(p, NULL);
3044 semsg(_("E244: Illegal quality name \"%s\" in font name \"%s\""), s, name);
3045 vim_free(s);
Bram Moolenaar7c1c6db2016-04-03 22:08:05 +02003046 break;
3047 }
3048 break;
3049 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003050 default:
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00003051 if (verbose)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003052 semsg(_("E245: Illegal char '%c' in font name \"%s\""), p[-1], name);
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003053 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054 }
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003055 while (*p == L':')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056 p++;
3057 }
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003058 ret = OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003059
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060theend:
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061 /* ron: init lastlf */
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003062 if (ret == OK && printer_dc == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003063 {
3064 vim_free(lastlf);
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003065 lastlf = (LOGFONTW *)alloc(sizeof(LOGFONTW));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 if (lastlf != NULL)
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003067 mch_memmove(lastlf, lf, sizeof(LOGFONTW));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068 }
Bram Moolenaar433a5eb2019-03-30 16:24:16 +01003069 vim_free(wname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003070
Bram Moolenaarb1692e22014-03-12 19:24:37 +01003071 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072}
3073
3074#endif /* defined(FEAT_GUI) || defined(FEAT_PRINTER) */
Bram Moolenaarf12d9832016-01-29 21:11:25 +01003075
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003076#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
Bram Moolenaarf12d9832016-01-29 21:11:25 +01003077/*
3078 * Initialize the Winsock dll.
3079 */
3080 void
Bram Moolenaar05540972016-01-30 20:31:25 +01003081channel_init_winsock(void)
Bram Moolenaarf12d9832016-01-29 21:11:25 +01003082{
3083 WSADATA wsaData;
3084 int wsaerr;
3085
3086 if (WSInitialized)
3087 return;
3088
3089 wsaerr = WSAStartup(MAKEWORD(2, 2), &wsaData);
3090 if (wsaerr == 0)
3091 WSInitialized = TRUE;
3092}
3093#endif