blob: c7eab50d5192fb5f86ad35c2e5bdbd4ab48c7ef1 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
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 *
13 * Routines common to both Win16 and Win32.
14 */
15
16#ifdef WIN16
17# ifdef __BORLANDC__
18# pragma warn -par
19# pragma warn -ucp
20# pragma warn -use
21# pragma warn -aus
22# endif
23#endif
24
Bram Moolenaar071d4272004-06-13 20:20:40 +000025#include "vim.h"
26
Bram Moolenaar071d4272004-06-13 20:20:40 +000027#ifdef WIN16
28# define SHORT_FNAME /* always 8.3 file name */
Bram Moolenaar82881492012-11-20 16:53:39 +010029/* cproto fails on missing include files */
30# ifndef PROTO
31# include <dos.h>
32# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000033# include <string.h>
34#endif
35#include <sys/types.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000036#include <signal.h>
37#include <limits.h>
Bram Moolenaar82881492012-11-20 16:53:39 +010038#ifndef PROTO
39# include <process.h>
40#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000041
42#undef chdir
43#ifdef __GNUC__
44# ifndef __MINGW32__
45# include <dirent.h>
46# endif
47#else
48# include <direct.h>
49#endif
50
Bram Moolenaar82881492012-11-20 16:53:39 +010051#ifndef PROTO
52# if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
53# include <shellapi.h>
54# endif
55
56# if defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)
57# include <dlgs.h>
58# ifdef WIN3264
59# include <winspool.h>
60# else
61# include <print.h>
62# endif
63# include <commdlg.h>
Bram Moolenaar071d4272004-06-13 20:20:40 +000064#endif
65
Bram Moolenaar82881492012-11-20 16:53:39 +010066#endif /* PROTO */
Bram Moolenaar071d4272004-06-13 20:20:40 +000067
68#ifdef __MINGW32__
69# ifndef FROM_LEFT_1ST_BUTTON_PRESSED
70# define FROM_LEFT_1ST_BUTTON_PRESSED 0x0001
71# endif
72# ifndef RIGHTMOST_BUTTON_PRESSED
73# define RIGHTMOST_BUTTON_PRESSED 0x0002
74# endif
75# ifndef FROM_LEFT_2ND_BUTTON_PRESSED
76# define FROM_LEFT_2ND_BUTTON_PRESSED 0x0004
77# endif
78# ifndef FROM_LEFT_3RD_BUTTON_PRESSED
79# define FROM_LEFT_3RD_BUTTON_PRESSED 0x0008
80# endif
81# ifndef FROM_LEFT_4TH_BUTTON_PRESSED
82# define FROM_LEFT_4TH_BUTTON_PRESSED 0x0010
83# endif
84
85/*
86 * EventFlags
87 */
88# ifndef MOUSE_MOVED
89# define MOUSE_MOVED 0x0001
90# endif
91# ifndef DOUBLE_CLICK
92# define DOUBLE_CLICK 0x0002
93# endif
94#endif
95
96/*
97 * When generating prototypes for Win32 on Unix, these lines make the syntax
98 * errors disappear. They do not need to be correct.
99 */
100#ifdef PROTO
101#define WINAPI
102#define WINBASEAPI
103typedef int BOOL;
104typedef int CALLBACK;
105typedef int COLORREF;
106typedef int CONSOLE_CURSOR_INFO;
107typedef int COORD;
108typedef int DWORD;
109typedef int ENUMLOGFONT;
110typedef int HANDLE;
111typedef int HDC;
112typedef int HFONT;
113typedef int HICON;
114typedef int HWND;
115typedef int INPUT_RECORD;
116typedef int KEY_EVENT_RECORD;
117typedef int LOGFONT;
118typedef int LPARAM;
119typedef int LPBOOL;
120typedef int LPCSTR;
121typedef int LPCWSTR;
122typedef int LPSTR;
123typedef int LPTSTR;
124typedef int LPWSTR;
125typedef int LRESULT;
126typedef int MOUSE_EVENT_RECORD;
127typedef int NEWTEXTMETRIC;
128typedef int PACL;
129typedef int PRINTDLG;
130typedef int PSECURITY_DESCRIPTOR;
131typedef int PSID;
132typedef int SECURITY_INFORMATION;
133typedef int SHORT;
134typedef int SMALL_RECT;
135typedef int TEXTMETRIC;
136typedef int UINT;
137typedef int WCHAR;
138typedef int WORD;
139typedef int WPARAM;
140typedef void VOID;
141#endif
142
143/* Record all output and all keyboard & mouse input */
144/* #define MCH_WRITE_DUMP */
145
146#ifdef MCH_WRITE_DUMP
147FILE* fdDump = NULL;
148#endif
149
150#ifdef WIN3264
151extern DWORD g_PlatformId;
152#endif
153
154#ifndef FEAT_GUI_MSWIN
155extern char g_szOrigTitle[];
156#endif
157
158#ifdef FEAT_GUI
159extern HWND s_hwnd;
160#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000161static HWND s_hwnd = 0; /* console window handle, set by GetConsoleHwnd() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000162#endif
163
164extern int WSInitialized;
165
166/* Don't generate prototypes here, because some systems do have these
167 * functions. */
168#if defined(__GNUC__) && !defined(PROTO)
169# ifndef __MINGW32__
170int _stricoll(char *a, char *b)
171{
172 // the ANSI-ish correct way is to use strxfrm():
173 char a_buff[512], b_buff[512]; // file names, so this is enough on Win32
174 strxfrm(a_buff, a, 512);
175 strxfrm(b_buff, b, 512);
176 return strcoll(a_buff, b_buff);
177}
178
179char * _fullpath(char *buf, char *fname, int len)
180{
181 LPTSTR toss;
182
183 return (char *)GetFullPathName(fname, len, buf, &toss);
184}
185# endif
186
Bram Moolenaaraf62ff32013-03-19 14:48:29 +0100187# if !defined(__MINGW32__) || (__GNUC__ < 4)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188int _chdrive(int drive)
189{
190 char temp [3] = "-:";
191 temp[0] = drive + 'A' - 1;
192 return !SetCurrentDirectory(temp);
193}
Bram Moolenaaraf62ff32013-03-19 14:48:29 +0100194# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000195#else
196# ifdef __BORLANDC__
197/* being a more ANSI compliant compiler, BorlandC doesn't define _stricoll:
198 * but it does in BC 5.02! */
199# if __BORLANDC__ < 0x502
200int _stricoll(char *a, char *b)
201{
202# if 1
203 // this is fast but not correct:
204 return stricmp(a, b);
205# else
206 // the ANSI-ish correct way is to use strxfrm():
207 char a_buff[512], b_buff[512]; // file names, so this is enough on Win32
208 strxfrm(a_buff, a, 512);
209 strxfrm(b_buff, b, 512);
210 return strcoll(a_buff, b_buff);
211# endif
212}
213# endif
214# endif
215#endif
216
217
218#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
219/*
220 * GUI version of mch_exit().
221 * Shut down and exit with status `r'
222 * Careful: mch_exit() may be called before mch_init()!
223 */
224 void
225mch_exit(int r)
226{
227 display_errors();
228
229 ml_close_all(TRUE); /* remove all memfiles */
230
231# ifdef FEAT_OLE
232 UninitOLE();
233# endif
234# ifdef FEAT_NETBEANS_INTG
235 if (WSInitialized)
236 {
237 WSInitialized = FALSE;
238 WSACleanup();
239 }
240# endif
241#ifdef DYNAMIC_GETTEXT
242 dyn_libintl_end();
243#endif
244
245 if (gui.in_use)
246 gui_exit(r);
Bram Moolenaar85c79d32007-02-20 01:59:20 +0000247
248#ifdef EXITFREE
249 free_all_mem();
250#endif
251
Bram Moolenaar071d4272004-06-13 20:20:40 +0000252 exit(r);
253}
254
255#endif /* FEAT_GUI_MSWIN */
256
257
258/*
259 * Init the tables for toupper() and tolower().
260 */
261 void
262mch_early_init(void)
263{
264 int i;
265
266#ifdef WIN3264
267 PlatformId();
268#endif
269
270 /* Init the tables for toupper() and tolower() */
271 for (i = 0; i < 256; ++i)
272 toupper_tab[i] = tolower_tab[i] = i;
273#ifdef WIN3264
274 CharUpperBuff(toupper_tab, 256);
275 CharLowerBuff(tolower_tab, 256);
276#else
277 AnsiUpperBuff(toupper_tab, 256);
278 AnsiLowerBuff(tolower_tab, 256);
279#endif
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000280
281#if defined(FEAT_MBYTE) && !defined(FEAT_GUI)
282 (void)get_cmd_argsW(NULL);
283#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284}
285
286
287/*
288 * Return TRUE if the input comes from a terminal, FALSE otherwise.
289 */
290 int
291mch_input_isatty()
292{
293#ifdef FEAT_GUI_MSWIN
294 return OK; /* GUI always has a tty */
295#else
296 if (isatty(read_cmd_fd))
297 return TRUE;
298 return FALSE;
299#endif
300}
301
302#ifdef FEAT_TITLE
303/*
304 * mch_settitle(): set titlebar of our window
305 */
306 void
307mch_settitle(
308 char_u *title,
309 char_u *icon)
310{
311# ifdef FEAT_GUI_MSWIN
312 gui_mch_settitle(title, icon);
313# else
314 if (title != NULL)
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000315 {
316# ifdef FEAT_MBYTE
317 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
318 {
319 /* Convert the title from 'encoding' to the active codepage. */
Bram Moolenaar36f692d2008-11-20 16:10:17 +0000320 WCHAR *wp = enc_to_utf16(title, NULL);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000321 int n;
322
323 if (wp != NULL)
324 {
325 n = SetConsoleTitleW(wp);
326 vim_free(wp);
327 if (n != 0 || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
328 return;
329 }
330 }
331# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000332 SetConsoleTitle(title);
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000333 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334# endif
335}
336
337
338/*
339 * Restore the window/icon title.
340 * which is one of:
341 * 1: Just restore title
342 * 2: Just restore icon (which we don't have)
343 * 3: Restore title and icon (which we don't have)
344 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000345/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346 void
347mch_restore_title(
348 int which)
349{
350#ifndef FEAT_GUI_MSWIN
351 mch_settitle((which & 1) ? g_szOrigTitle : NULL, NULL);
352#endif
353}
354
355
356/*
357 * Return TRUE if we can restore the title (we can)
358 */
359 int
360mch_can_restore_title()
361{
362 return TRUE;
363}
364
365
366/*
367 * Return TRUE if we can restore the icon title (we can't)
368 */
369 int
370mch_can_restore_icon()
371{
372 return FALSE;
373}
374#endif /* FEAT_TITLE */
375
376
377/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000378 * Get absolute file name into buffer "buf" of length "len" bytes,
379 * turning all '/'s into '\\'s and getting the correct case of each component
380 * of the file name. Append a (back)slash to a directory name.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381 * When 'shellslash' set do it the other way around.
382 * Return OK or FAIL.
383 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000384/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385 int
386mch_FullName(
387 char_u *fname,
388 char_u *buf,
389 int len,
390 int force)
391{
392 int nResult = FAIL;
393
394#ifdef __BORLANDC__
395 if (*fname == NUL) /* Borland behaves badly here - make it consistent */
396 nResult = mch_dirname(buf, len);
397 else
398#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000399 {
Bram Moolenaar19a09a12005-03-04 23:39:37 +0000400#ifdef FEAT_MBYTE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000401 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
402# ifdef __BORLANDC__
403 /* Wide functions of Borland C 5.5 do not work on Windows 98. */
404 && g_PlatformId == VER_PLATFORM_WIN32_NT
405# endif
406 )
407 {
408 WCHAR *wname;
409 WCHAR wbuf[MAX_PATH];
410 char_u *cname = NULL;
411
412 /* Use the wide function:
413 * - convert the fname from 'encoding' to UCS2.
414 * - invoke _wfullpath()
415 * - convert the result from UCS2 to 'encoding'.
416 */
Bram Moolenaar36f692d2008-11-20 16:10:17 +0000417 wname = enc_to_utf16(fname, NULL);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000418 if (wname != NULL && _wfullpath(wbuf, wname, MAX_PATH - 1) != NULL)
419 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +0000420 cname = utf16_to_enc((short_u *)wbuf, NULL);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000421 if (cname != NULL)
422 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +0000423 vim_strncpy(buf, cname, len - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000424 nResult = OK;
425 }
426 }
427 vim_free(wname);
428 vim_free(cname);
429 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000430 if (nResult == FAIL) /* fall back to non-wide function */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000431#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000432 {
433 if (_fullpath(buf, fname, len - 1) == NULL)
434 {
Bram Moolenaarfe3ca8d2005-07-18 21:43:02 +0000435 /* failed, use relative path name */
436 vim_strncpy(buf, fname, len - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000437 }
438 else
439 nResult = OK;
440 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442
443#ifdef USE_FNAME_CASE
444 fname_case(buf, len);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000445#else
446 slash_adjust(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447#endif
448
449 return nResult;
450}
451
452
453/*
454 * Return TRUE if "fname" does not depend on the current directory.
455 */
456 int
457mch_isFullName(char_u *fname)
458{
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200459#ifdef FEAT_MBYTE
460 /* WinNT and later can use _MAX_PATH wide characters for a pathname, which
461 * means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
462 * UTF-8. */
463 char szName[_MAX_PATH * 3 + 1];
464#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465 char szName[_MAX_PATH + 1];
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200466#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000467
468 /* A name like "d:/foo" and "//server/share" is absolute */
469 if ((fname[0] && fname[1] == ':' && (fname[2] == '/' || fname[2] == '\\'))
470 || (fname[0] == fname[1] && (fname[0] == '/' || fname[0] == '\\')))
471 return TRUE;
472
473 /* A name that can't be made absolute probably isn't absolute. */
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200474 if (mch_FullName(fname, szName, sizeof(szName) - 1, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 return FALSE;
476
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000477 return pathcmp(fname, szName, -1) == 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478}
479
480/*
481 * Replace all slashes by backslashes.
482 * This used to be the other way around, but MS-DOS sometimes has problems
483 * with slashes (e.g. in a command name). We can't have mixed slashes and
484 * backslashes, because comparing file names will not work correctly. The
485 * commands that use a file name should try to avoid the need to type a
486 * backslash twice.
487 * When 'shellslash' set do it the other way around.
488 */
489 void
490slash_adjust(p)
491 char_u *p;
492{
Bram Moolenaara3ffd9c2005-07-21 21:03:15 +0000493 while (*p)
494 {
495 if (*p == psepcN)
496 *p = psepc;
497 mb_ptr_adv(p);
498 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000499}
500
Bram Moolenaar9d1685d2014-01-14 12:18:45 +0100501#if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__MINGW32__)
Bram Moolenaar8962fda2013-09-29 19:05:21 +0200502# define OPEN_OH_ARGTYPE intptr_t
503#else
504# define OPEN_OH_ARGTYPE long
505#endif
506
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200507 static int
508stat_symlink_aware(const char *name, struct stat *stp)
509{
510#if defined(_MSC_VER) && _MSC_VER < 1700
511 /* Work around for VC10 or earlier. stat() can't handle symlinks properly.
512 * VC9 or earlier: stat() doesn't support a symlink at all. It retrieves
513 * status of a symlink itself.
514 * VC10: stat() supports a symlink to a normal file, but it doesn't support
515 * a symlink to a directory (always returns an error). */
516 WIN32_FIND_DATA findData;
517 HANDLE hFind, h;
518 DWORD attr = 0;
519 BOOL is_symlink = FALSE;
520
521 hFind = FindFirstFile(name, &findData);
522 if (hFind != INVALID_HANDLE_VALUE)
523 {
524 attr = findData.dwFileAttributes;
525 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
526 && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
527 is_symlink = TRUE;
528 FindClose(hFind);
529 }
530 if (is_symlink)
531 {
532 h = CreateFile(name, FILE_READ_ATTRIBUTES,
533 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
534 OPEN_EXISTING,
535 (attr & FILE_ATTRIBUTE_DIRECTORY)
536 ? FILE_FLAG_BACKUP_SEMANTICS : 0,
537 NULL);
538 if (h != INVALID_HANDLE_VALUE)
539 {
540 int fd, n;
541
Bram Moolenaar8962fda2013-09-29 19:05:21 +0200542 fd = _open_osfhandle((OPEN_OH_ARGTYPE)h, _O_RDONLY);
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200543 n = _fstat(fd, (struct _stat*)stp);
544 _close(fd);
545 return n;
546 }
547 }
548#endif
549 return stat(name, stp);
550}
551
552#ifdef FEAT_MBYTE
553 static int
554wstat_symlink_aware(const WCHAR *name, struct _stat *stp)
555{
556# if defined(_MSC_VER) && _MSC_VER < 1700
557 /* Work around for VC10 or earlier. _wstat() can't handle symlinks properly.
558 * VC9 or earlier: _wstat() doesn't support a symlink at all. It retrieves
559 * status of a symlink itself.
560 * VC10: _wstat() supports a symlink to a normal file, but it doesn't
561 * support a symlink to a directory (always returns an error). */
562 int n;
563 BOOL is_symlink = FALSE;
564 HANDLE hFind, h;
565 DWORD attr = 0;
566 WIN32_FIND_DATAW findDataW;
567
568 hFind = FindFirstFileW(name, &findDataW);
569 if (hFind != INVALID_HANDLE_VALUE)
570 {
571 attr = findDataW.dwFileAttributes;
572 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
573 && (findDataW.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
574 is_symlink = TRUE;
575 FindClose(hFind);
576 }
577 if (is_symlink)
578 {
579 h = CreateFileW(name, FILE_READ_ATTRIBUTES,
580 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
581 OPEN_EXISTING,
582 (attr & FILE_ATTRIBUTE_DIRECTORY)
583 ? FILE_FLAG_BACKUP_SEMANTICS : 0,
584 NULL);
585 if (h != INVALID_HANDLE_VALUE)
586 {
587 int fd;
588
Bram Moolenaar8962fda2013-09-29 19:05:21 +0200589 fd = _open_osfhandle((OPEN_OH_ARGTYPE)h, _O_RDONLY);
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200590 n = _fstat(fd, stp);
591 _close(fd);
592 return n;
593 }
594 }
595# endif
596 return _wstat(name, stp);
597}
598#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000599
600/*
601 * stat() can't handle a trailing '/' or '\', remove it first.
602 */
603 int
604vim_stat(const char *name, struct stat *stp)
605{
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200606#ifdef FEAT_MBYTE
607 /* WinNT and later can use _MAX_PATH wide characters for a pathname, which
608 * means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
609 * UTF-8. */
610 char buf[_MAX_PATH * 3 + 1];
611#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612 char buf[_MAX_PATH + 1];
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200613#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614 char *p;
615
Bram Moolenaard2a203b2013-08-30 16:51:18 +0200616 vim_strncpy((char_u *)buf, (char_u *)name, sizeof(buf) - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 p = buf + strlen(buf);
618 if (p > buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000619 mb_ptr_back(buf, p);
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100620
621 /* Remove trailing '\\' except root path. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 if (p > buf && (*p == '\\' || *p == '/') && p[-1] != ':')
623 *p = NUL;
Bram Moolenaarb1cb35f2014-01-10 13:05:20 +0100624
625 if ((buf[0] == '\\' && buf[1] == '\\') || (buf[0] == '/' && buf[1] == '/'))
626 {
627 /* UNC root path must be followed by '\\'. */
628 p = vim_strpbrk(buf + 2, "\\/");
629 if (p != NULL)
630 {
631 p = vim_strpbrk(p + 1, "\\/");
632 if (p == NULL)
633 STRCAT(buf, "\\");
634 }
635 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636#ifdef FEAT_MBYTE
637 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
638# ifdef __BORLANDC__
639 /* Wide functions of Borland C 5.5 do not work on Windows 98. */
640 && g_PlatformId == VER_PLATFORM_WIN32_NT
641# endif
642 )
643 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +0000644 WCHAR *wp = enc_to_utf16(buf, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000645 int n;
646
647 if (wp != NULL)
648 {
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200649 n = wstat_symlink_aware(wp, (struct _stat *)stp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650 vim_free(wp);
651 if (n >= 0)
652 return n;
653 /* Retry with non-wide function (for Windows 98). Can't use
654 * GetLastError() here and it's unclear what errno gets set to if
655 * the _wstat() fails for missing wide functions. */
656 }
657 }
658#endif
Bram Moolenaar2ee95f72013-09-25 19:13:38 +0200659 return stat_symlink_aware(buf, stp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000660}
661
662#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000663/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000664 void
665mch_settmode(int tmode)
666{
667 /* nothing to do */
668}
669
670 int
671mch_get_shellsize(void)
672{
673 /* never used */
674 return OK;
675}
676
677 void
678mch_set_shellsize(void)
679{
680 /* never used */
681}
682
683/*
684 * Rows and/or Columns has changed.
685 */
686 void
687mch_new_shellsize(void)
688{
689 /* never used */
690}
691
692#endif
693
694/*
695 * We have no job control, so fake it by starting a new shell.
696 */
697 void
698mch_suspend()
699{
700 suspend_shell();
701}
702
703#if defined(USE_MCH_ERRMSG) || defined(PROTO)
704
705#ifdef display_errors
706# undef display_errors
707#endif
708
709/*
710 * Display the saved error message(s).
711 */
712 void
713display_errors()
714{
715 char *p;
716
717 if (error_ga.ga_data != NULL)
718 {
719 /* avoid putting up a message box with blanks only */
720 for (p = (char *)error_ga.ga_data; *p; ++p)
721 if (!isspace(*p))
722 {
Bram Moolenaarc17ef8e2006-03-25 21:48:58 +0000723 (void)gui_mch_dialog(
724#ifdef FEAT_GUI
725 gui.starting ? VIM_INFO :
726#endif
727 VIM_ERROR,
728#ifdef FEAT_GUI
729 gui.starting ? (char_u *)_("Message") :
730#endif
731 (char_u *)_("Error"),
Bram Moolenaard2c340a2011-01-17 20:08:11 +0100732 p, (char_u *)_("&Ok"), 1, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 break;
734 }
735 ga_clear(&error_ga);
736 }
737}
738#endif
739
740
741/*
742 * Return TRUE if "p" contain a wildcard that can be expanded by
743 * dos_expandpath().
744 */
745 int
746mch_has_exp_wildcard(char_u *p)
747{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000748 for ( ; *p; mb_ptr_adv(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749 {
750 if (vim_strchr((char_u *)"?*[", *p) != NULL
751 || (*p == '~' && p[1] != NUL))
752 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000753 }
754 return FALSE;
755}
756
757/*
758 * Return TRUE if "p" contain a wildcard or a "~1" kind of thing (could be a
759 * shortened file name).
760 */
761 int
762mch_has_wildcard(char_u *p)
763{
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000764 for ( ; *p; mb_ptr_adv(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 {
766 if (vim_strchr((char_u *)
767# ifdef VIM_BACKTICK
768 "?*$[`"
769# else
770 "?*$["
771# endif
772 , *p) != NULL
773 || (*p == '~' && p[1] != NUL))
774 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000775 }
776 return FALSE;
777}
778
779
780/*
781 * The normal _chdir() does not change the default drive. This one does.
782 * Returning 0 implies success; -1 implies failure.
783 */
784 int
785mch_chdir(char *path)
786{
787 if (path[0] == NUL) /* just checking... */
788 return -1;
789
Bram Moolenaara2974d72009-07-14 16:38:36 +0000790 if (p_verbose >= 5)
791 {
792 verbose_enter();
793 smsg((char_u *)"chdir(%s)", path);
794 verbose_leave();
795 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000796 if (isalpha(path[0]) && path[1] == ':') /* has a drive name */
797 {
798 /* If we can change to the drive, skip that part of the path. If we
799 * can't then the current directory may be invalid, try using chdir()
800 * with the whole path. */
801 if (_chdrive(TOLOWER_ASC(path[0]) - 'a' + 1) == 0)
802 path += 2;
803 }
804
805 if (*path == NUL) /* drive name only */
806 return 0;
807
Bram Moolenaar15d0a8c2004-09-06 17:44:46 +0000808#ifdef FEAT_MBYTE
809 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
810 {
Bram Moolenaar36f692d2008-11-20 16:10:17 +0000811 WCHAR *p = enc_to_utf16(path, NULL);
Bram Moolenaar15d0a8c2004-09-06 17:44:46 +0000812 int n;
813
814 if (p != NULL)
815 {
816 n = _wchdir(p);
817 vim_free(p);
818 if (n == 0)
819 return 0;
820 /* Retry with non-wide function (for Windows 98). */
821 }
822 }
823#endif
824
Bram Moolenaar071d4272004-06-13 20:20:40 +0000825 return chdir(path); /* let the normal chdir() do the rest */
826}
827
828
829/*
830 * Switching off termcap mode is only allowed when Columns is 80, otherwise a
831 * crash may result. It's always allowed on NT or when running the GUI.
832 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000833/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000834 int
835can_end_termcap_mode(
836 int give_msg)
837{
838#ifdef FEAT_GUI_MSWIN
839 return TRUE; /* GUI starts a new console anyway */
840#else
841 if (g_PlatformId == VER_PLATFORM_WIN32_NT || Columns == 80)
842 return TRUE;
843 if (give_msg)
844 msg(_("'columns' is not 80, cannot execute external commands"));
845 return FALSE;
846#endif
847}
848
849#ifdef FEAT_GUI_MSWIN
850/*
851 * return non-zero if a character is available
852 */
853 int
854mch_char_avail()
855{
856 /* never used */
857 return TRUE;
858}
859#endif
860
861
862/*
863 * set screen mode, always fails.
864 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +0000865/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 int
867mch_screenmode(
868 char_u *arg)
869{
870 EMSG(_(e_screenmode));
871 return FAIL;
872}
873
874
875#if defined(FEAT_LIBCALL) || defined(PROTO)
876/*
877 * Call a DLL routine which takes either a string or int param
878 * and returns an allocated string.
879 * Return OK if it worked, FAIL if not.
880 */
881# ifdef WIN3264
882typedef LPTSTR (*MYSTRPROCSTR)(LPTSTR);
883typedef LPTSTR (*MYINTPROCSTR)(int);
884typedef int (*MYSTRPROCINT)(LPTSTR);
885typedef int (*MYINTPROCINT)(int);
886# else
887typedef LPSTR (*MYSTRPROCSTR)(LPSTR);
888typedef LPSTR (*MYINTPROCSTR)(int);
889typedef int (*MYSTRPROCINT)(LPSTR);
890typedef int (*MYINTPROCINT)(int);
891# endif
892
893# ifndef WIN16
894/*
895 * Check if a pointer points to a valid NUL terminated string.
896 * Return the length of the string, including terminating NUL.
897 * Returns 0 for an invalid pointer, 1 for an empty string.
898 */
899 static size_t
900check_str_len(char_u *str)
901{
902 SYSTEM_INFO si;
903 MEMORY_BASIC_INFORMATION mbi;
904 size_t length = 0;
905 size_t i;
906 const char *p;
907
908 /* get page size */
909 GetSystemInfo(&si);
910
911 /* get memory information */
912 if (VirtualQuery(str, &mbi, sizeof(mbi)))
913 {
914 /* pre cast these (typing savers) */
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000915 long_u dwStr = (long_u)str;
916 long_u dwBaseAddress = (long_u)mbi.BaseAddress;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000917
918 /* get start address of page that str is on */
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000919 long_u strPage = dwStr - (dwStr - dwBaseAddress) % si.dwPageSize;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000920
921 /* get length from str to end of page */
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000922 long_u pageLength = si.dwPageSize - (dwStr - strPage);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000923
Bram Moolenaar442b4222010-05-24 21:34:22 +0200924 for (p = str; !IsBadReadPtr(p, (UINT)pageLength);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000925 p += pageLength, pageLength = si.dwPageSize)
926 for (i = 0; i < pageLength; ++i, ++length)
927 if (p[i] == NUL)
928 return length + 1;
929 }
930
931 return 0;
932}
933# endif
934
935 int
936mch_libcall(
937 char_u *libname,
938 char_u *funcname,
939 char_u *argstring, /* NULL when using a argint */
940 int argint,
941 char_u **string_result,/* NULL when using number_result */
942 int *number_result)
943{
944 HINSTANCE hinstLib;
945 MYSTRPROCSTR ProcAdd;
946 MYINTPROCSTR ProcAddI;
947 char_u *retval_str = NULL;
948 int retval_int = 0;
949 size_t len;
950
951 BOOL fRunTimeLinkSuccess = FALSE;
952
953 // Get a handle to the DLL module.
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200954# ifdef WIN16
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955 hinstLib = LoadLibrary(libname);
Bram Moolenaarebbcb822010-10-23 14:02:54 +0200956# else
957 hinstLib = vimLoadLib(libname);
958# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959
960 // If the handle is valid, try to get the function address.
961 if (hinstLib != NULL)
962 {
963#ifdef HAVE_TRY_EXCEPT
964 __try
965 {
966#endif
967 if (argstring != NULL)
968 {
969 /* Call with string argument */
970 ProcAdd = (MYSTRPROCSTR) GetProcAddress(hinstLib, funcname);
971 if ((fRunTimeLinkSuccess = (ProcAdd != NULL)) != 0)
972 {
973 if (string_result == NULL)
974 retval_int = ((MYSTRPROCINT)ProcAdd)(argstring);
975 else
976 retval_str = (ProcAdd)(argstring);
977 }
978 }
979 else
980 {
981 /* Call with number argument */
982 ProcAddI = (MYINTPROCSTR) GetProcAddress(hinstLib, funcname);
983 if ((fRunTimeLinkSuccess = (ProcAddI != NULL)) != 0)
984 {
985 if (string_result == NULL)
986 retval_int = ((MYINTPROCINT)ProcAddI)(argint);
987 else
988 retval_str = (ProcAddI)(argint);
989 }
990 }
991
992 // Save the string before we free the library.
993 // Assume that a "1" result is an illegal pointer.
994 if (string_result == NULL)
995 *number_result = retval_int;
996 else if (retval_str != NULL
997# ifdef WIN16
998 && retval_str != (char_u *)1
999 && retval_str != (char_u *)-1
1000 && !IsBadStringPtr(retval_str, INT_MAX)
1001 && (len = strlen(retval_str) + 1) > 0
1002# else
1003 && (len = check_str_len(retval_str)) > 0
1004# endif
1005 )
1006 {
1007 *string_result = lalloc((long_u)len, TRUE);
1008 if (*string_result != NULL)
1009 mch_memmove(*string_result, retval_str, len);
1010 }
1011
1012#ifdef HAVE_TRY_EXCEPT
1013 }
1014 __except(EXCEPTION_EXECUTE_HANDLER)
1015 {
1016 if (GetExceptionCode() == EXCEPTION_STACK_OVERFLOW)
1017 RESETSTKOFLW();
1018 fRunTimeLinkSuccess = 0;
1019 }
1020#endif
1021
1022 // Free the DLL module.
1023 (void)FreeLibrary(hinstLib);
1024 }
1025
1026 if (!fRunTimeLinkSuccess)
1027 {
1028 EMSG2(_(e_libcall), funcname);
1029 return FAIL;
1030 }
1031
1032 return OK;
1033}
1034#endif
1035
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036/*
1037 * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
1038 */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00001039/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 void
1041DumpPutS(
1042 const char *psz)
1043{
1044# ifdef MCH_WRITE_DUMP
1045 if (fdDump)
1046 {
1047 fputs(psz, fdDump);
1048 if (psz[strlen(psz) - 1] != '\n')
1049 fputc('\n', fdDump);
1050 fflush(fdDump);
1051 }
1052# endif
1053}
1054
1055#ifdef _DEBUG
1056
1057void __cdecl
1058Trace(
1059 char *pszFormat,
1060 ...)
1061{
1062 CHAR szBuff[2048];
1063 va_list args;
1064
1065 va_start(args, pszFormat);
1066 vsprintf(szBuff, pszFormat, args);
1067 va_end(args);
1068
1069 OutputDebugString(szBuff);
1070}
1071
1072#endif //_DEBUG
1073
Bram Moolenaar843ee412004-06-30 16:16:41 +00001074#if !defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075# if defined(FEAT_TITLE) && defined(WIN3264)
1076extern HWND g_hWnd; /* This is in os_win32.c. */
1077# endif
1078
1079/*
1080 * Showing the printer dialog is tricky since we have no GUI
1081 * window to parent it. The following routines are needed to
1082 * get the window parenting and Z-order to work properly.
1083 */
1084 static void
1085GetConsoleHwnd(void)
1086{
1087# define MY_BUFSIZE 1024 // Buffer size for console window titles.
1088
1089 char pszNewWindowTitle[MY_BUFSIZE]; // Contains fabricated WindowTitle.
1090 char pszOldWindowTitle[MY_BUFSIZE]; // Contains original WindowTitle.
1091
1092 /* Skip if it's already set. */
1093 if (s_hwnd != 0)
1094 return;
1095
1096# if defined(FEAT_TITLE) && defined(WIN3264)
1097 /* Window handle may have been found by init code (Windows NT only) */
1098 if (g_hWnd != 0)
1099 {
1100 s_hwnd = g_hWnd;
1101 return;
1102 }
1103# endif
1104
1105 GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE);
1106
1107 wsprintf(pszNewWindowTitle, "%s/%d/%d",
1108 pszOldWindowTitle,
1109 GetTickCount(),
1110 GetCurrentProcessId());
1111 SetConsoleTitle(pszNewWindowTitle);
1112 Sleep(40);
1113 s_hwnd = FindWindow(NULL, pszNewWindowTitle);
1114
1115 SetConsoleTitle(pszOldWindowTitle);
1116}
Bram Moolenaar843ee412004-06-30 16:16:41 +00001117
1118/*
1119 * Console implementation of ":winpos".
1120 */
1121 int
1122mch_get_winpos(int *x, int *y)
1123{
1124 RECT rect;
1125
1126 GetConsoleHwnd();
1127 GetWindowRect(s_hwnd, &rect);
1128 *x = rect.left;
1129 *y = rect.top;
1130 return OK;
1131}
1132
1133/*
1134 * Console implementation of ":winpos x y".
1135 */
1136 void
1137mch_set_winpos(int x, int y)
1138{
1139 GetConsoleHwnd();
1140 SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1141 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1142}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001143#endif
1144
1145#if (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) || defined(PROTO)
1146
1147# ifdef WIN16
1148# define TEXT(a) a
1149# endif
1150/*=================================================================
1151 * Win32 printer stuff
1152 */
1153
1154static HFONT prt_font_handles[2][2][2];
1155static PRINTDLG prt_dlg;
1156static const int boldface[2] = {FW_REGULAR, FW_BOLD};
1157static TEXTMETRIC prt_tm;
1158static int prt_line_height;
1159static int prt_number_width;
1160static int prt_left_margin;
1161static int prt_right_margin;
1162static int prt_top_margin;
1163static char_u szAppName[] = TEXT("VIM");
1164static HWND hDlgPrint;
1165static int *bUserAbort = NULL;
1166static char_u *prt_name = NULL;
1167
1168/* Defines which are also in vim.rc. */
1169#define IDC_BOX1 400
1170#define IDC_PRINTTEXT1 401
1171#define IDC_PRINTTEXT2 402
1172#define IDC_PROGRESS 403
1173
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001174#if !defined(FEAT_MBYTE) || defined(WIN16)
1175# define vimSetDlgItemText(h, i, s) SetDlgItemText(h, i, s)
1176#else
1177 static BOOL
1178vimSetDlgItemText(HWND hDlg, int nIDDlgItem, char_u *s)
1179{
1180 WCHAR *wp = NULL;
1181 BOOL ret;
1182
1183 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
1184 {
1185 wp = enc_to_utf16(s, NULL);
1186 }
1187 if (wp != NULL)
1188 {
1189 ret = SetDlgItemTextW(hDlg, nIDDlgItem, wp);
1190 vim_free(wp);
1191 return ret;
1192 }
1193 return SetDlgItemText(hDlg, nIDDlgItem, s);
1194}
1195#endif
1196
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197/*
1198 * Convert BGR to RGB for Windows GDI calls
1199 */
1200 static COLORREF
1201swap_me(COLORREF colorref)
1202{
1203 int temp;
1204 char *ptr = (char *)&colorref;
1205
1206 temp = *(ptr);
1207 *(ptr ) = *(ptr + 2);
1208 *(ptr + 2) = temp;
1209 return colorref;
1210}
1211
Bram Moolenaared39e1d2008-08-09 17:55:22 +00001212/* Attempt to make this work for old and new compilers */
Bram Moolenaar9f733d12011-09-21 20:09:42 +02001213#if !defined(_WIN64) && (!defined(_MSC_VER) || _MSC_VER < 1300)
Bram Moolenaared39e1d2008-08-09 17:55:22 +00001214# define PDP_RETVAL BOOL
1215#else
1216# define PDP_RETVAL INT_PTR
1217#endif
1218
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00001219/*ARGSUSED*/
Bram Moolenaared39e1d2008-08-09 17:55:22 +00001220 static PDP_RETVAL CALLBACK
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221PrintDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
1222{
1223#ifdef FEAT_GETTEXT
1224 NONCLIENTMETRICS nm;
1225 static HFONT hfont;
1226#endif
1227
1228 switch (message)
1229 {
1230 case WM_INITDIALOG:
1231#ifdef FEAT_GETTEXT
1232 nm.cbSize = sizeof(NONCLIENTMETRICS);
1233 if (SystemParametersInfo(
1234 SPI_GETNONCLIENTMETRICS,
1235 sizeof(NONCLIENTMETRICS),
1236 &nm,
1237 0))
1238 {
1239 char buff[MAX_PATH];
1240 int i;
1241
1242 /* Translate the dialog texts */
1243 hfont = CreateFontIndirect(&nm.lfMessageFont);
1244 for (i = IDC_PRINTTEXT1; i <= IDC_PROGRESS; i++)
1245 {
1246 SendDlgItemMessage(hDlg, i, WM_SETFONT, (WPARAM)hfont, 1);
1247 if (GetDlgItemText(hDlg,i, buff, sizeof(buff)))
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001248 vimSetDlgItemText(hDlg,i, _(buff));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 }
1250 SendDlgItemMessage(hDlg, IDCANCEL,
1251 WM_SETFONT, (WPARAM)hfont, 1);
1252 if (GetDlgItemText(hDlg,IDCANCEL, buff, sizeof(buff)))
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001253 vimSetDlgItemText(hDlg,IDCANCEL, _(buff));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 }
1255#endif
1256 SetWindowText(hDlg, szAppName);
1257 if (prt_name != NULL)
1258 {
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001259 vimSetDlgItemText(hDlg, IDC_PRINTTEXT2, (LPSTR)prt_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260 vim_free(prt_name);
1261 prt_name = NULL;
1262 }
1263 EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_CLOSE, MF_GRAYED);
1264#ifndef FEAT_GUI
1265 BringWindowToTop(s_hwnd);
1266#endif
1267 return TRUE;
1268
1269 case WM_COMMAND:
1270 *bUserAbort = TRUE;
1271 EnableWindow(GetParent(hDlg), TRUE);
1272 DestroyWindow(hDlg);
1273 hDlgPrint = NULL;
1274#ifdef FEAT_GETTEXT
1275 DeleteObject(hfont);
1276#endif
1277 return TRUE;
1278 }
1279 return FALSE;
1280}
1281
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00001282/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001283 static BOOL CALLBACK
1284AbortProc(HDC hdcPrn, int iCode)
1285{
1286 MSG msg;
1287
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02001288 while (!*bUserAbort && pPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289 {
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02001290 if (!hDlgPrint || !pIsDialogMessage(hDlgPrint, &msg))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291 {
1292 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02001293 pDispatchMessage(&msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001294 }
1295 }
1296 return !*bUserAbort;
1297}
1298
1299#ifndef FEAT_GUI
1300
Bram Moolenaar26fdd7d2011-11-30 13:42:44 +01001301 static UINT_PTR CALLBACK
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302PrintHookProc(
1303 HWND hDlg, // handle to dialog box
1304 UINT uiMsg, // message identifier
1305 WPARAM wParam, // message parameter
1306 LPARAM lParam // message parameter
1307 )
1308{
1309 HWND hwndOwner;
1310 RECT rc, rcDlg, rcOwner;
1311 PRINTDLG *pPD;
1312
1313 if (uiMsg == WM_INITDIALOG)
1314 {
1315 // Get the owner window and dialog box rectangles.
1316 if ((hwndOwner = GetParent(hDlg)) == NULL)
1317 hwndOwner = GetDesktopWindow();
1318
1319 GetWindowRect(hwndOwner, &rcOwner);
1320 GetWindowRect(hDlg, &rcDlg);
1321 CopyRect(&rc, &rcOwner);
1322
1323 // Offset the owner and dialog box rectangles so that
1324 // right and bottom values represent the width and
1325 // height, and then offset the owner again to discard
1326 // space taken up by the dialog box.
1327
1328 OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1329 OffsetRect(&rc, -rc.left, -rc.top);
1330 OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
1331
1332 // The new position is the sum of half the remaining
1333 // space and the owner's original position.
1334
1335 SetWindowPos(hDlg,
1336 HWND_TOP,
1337 rcOwner.left + (rc.right / 2),
1338 rcOwner.top + (rc.bottom / 2),
1339 0, 0, // ignores size arguments
1340 SWP_NOSIZE);
1341
1342 /* tackle the printdlg copiesctrl problem */
1343 pPD = (PRINTDLG *)lParam;
1344 pPD->nCopies = (WORD)pPD->lCustData;
1345 SetDlgItemInt( hDlg, edt3, pPD->nCopies, FALSE );
1346 /* Bring the window to top */
1347 BringWindowToTop(GetParent(hDlg));
1348 SetForegroundWindow(hDlg);
1349 }
1350
1351 return FALSE;
1352}
1353#endif
1354
1355 void
1356mch_print_cleanup(void)
1357{
1358 int pifItalic;
1359 int pifBold;
1360 int pifUnderline;
1361
1362 for (pifBold = 0; pifBold <= 1; pifBold++)
1363 for (pifItalic = 0; pifItalic <= 1; pifItalic++)
1364 for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
1365 DeleteObject(prt_font_handles[pifBold][pifItalic][pifUnderline]);
1366
1367 if (prt_dlg.hDC != NULL)
1368 DeleteDC(prt_dlg.hDC);
1369 if (!*bUserAbort)
1370 SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
1371}
1372
1373 static int
1374to_device_units(int idx, int dpi, int physsize, int offset, int def_number)
1375{
1376 int ret = 0;
1377 int u;
1378 int nr;
1379
1380 u = prt_get_unit(idx);
1381 if (u == PRT_UNIT_NONE)
1382 {
1383 u = PRT_UNIT_PERC;
1384 nr = def_number;
1385 }
1386 else
1387 nr = printer_opts[idx].number;
1388
1389 switch (u)
1390 {
1391 case PRT_UNIT_PERC:
1392 ret = (physsize * nr) / 100;
1393 break;
1394 case PRT_UNIT_INCH:
1395 ret = (nr * dpi);
1396 break;
1397 case PRT_UNIT_MM:
1398 ret = (nr * 10 * dpi) / 254;
1399 break;
1400 case PRT_UNIT_POINT:
1401 ret = (nr * 10 * dpi) / 720;
1402 break;
1403 }
1404
1405 if (ret < offset)
1406 return 0;
1407 else
1408 return ret - offset;
1409}
1410
1411 static int
1412prt_get_cpl(void)
1413{
1414 int hr;
1415 int phyw;
1416 int dvoff;
1417 int rev_offset;
1418 int dpi;
1419#ifdef WIN16
1420 POINT pagesize;
1421#endif
1422
1423 GetTextMetrics(prt_dlg.hDC, &prt_tm);
1424 prt_line_height = prt_tm.tmHeight + prt_tm.tmExternalLeading;
1425
1426 hr = GetDeviceCaps(prt_dlg.hDC, HORZRES);
1427#ifdef WIN16
1428 Escape(prt_dlg.hDC, GETPHYSPAGESIZE, NULL, NULL, &pagesize);
1429 phyw = pagesize.x;
1430 Escape(prt_dlg.hDC, GETPRINTINGOFFSET, NULL, NULL, &pagesize);
1431 dvoff = pagesize.x;
1432#else
1433 phyw = GetDeviceCaps(prt_dlg.hDC, PHYSICALWIDTH);
1434 dvoff = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETX);
1435#endif
1436 dpi = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSX);
1437
1438 rev_offset = phyw - (dvoff + hr);
1439
1440 prt_left_margin = to_device_units(OPT_PRINT_LEFT, dpi, phyw, dvoff, 10);
1441 if (prt_use_number())
1442 {
1443 prt_number_width = PRINT_NUMBER_WIDTH * prt_tm.tmAveCharWidth;
1444 prt_left_margin += prt_number_width;
1445 }
1446 else
1447 prt_number_width = 0;
1448
1449 prt_right_margin = hr - to_device_units(OPT_PRINT_RIGHT, dpi, phyw,
1450 rev_offset, 5);
1451
1452 return (prt_right_margin - prt_left_margin) / prt_tm.tmAveCharWidth;
1453}
1454
1455 static int
1456prt_get_lpp(void)
1457{
1458 int vr;
1459 int phyw;
1460 int dvoff;
1461 int rev_offset;
1462 int bottom_margin;
1463 int dpi;
1464#ifdef WIN16
1465 POINT pagesize;
1466#endif
1467
1468 vr = GetDeviceCaps(prt_dlg.hDC, VERTRES);
1469#ifdef WIN16
1470 Escape(prt_dlg.hDC, GETPHYSPAGESIZE, NULL, NULL, &pagesize);
1471 phyw = pagesize.y;
1472 Escape(prt_dlg.hDC, GETPRINTINGOFFSET, NULL, NULL, &pagesize);
1473 dvoff = pagesize.y;
1474#else
1475 phyw = GetDeviceCaps(prt_dlg.hDC, PHYSICALHEIGHT);
1476 dvoff = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETY);
1477#endif
1478 dpi = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSY);
1479
1480 rev_offset = phyw - (dvoff + vr);
1481
1482 prt_top_margin = to_device_units(OPT_PRINT_TOP, dpi, phyw, dvoff, 5);
1483
1484 /* adjust top margin if there is a header */
1485 prt_top_margin += prt_line_height * prt_header_height();
1486
1487 bottom_margin = vr - to_device_units(OPT_PRINT_BOT, dpi, phyw,
1488 rev_offset, 5);
1489
1490 return (bottom_margin - prt_top_margin) / prt_line_height;
1491}
1492
1493 int
1494mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
1495{
1496 static HGLOBAL stored_dm = NULL;
1497 static HGLOBAL stored_devn = NULL;
1498 static int stored_nCopies = 1;
1499 static int stored_nFlags = 0;
1500
1501 LOGFONT fLogFont;
1502 int pifItalic;
1503 int pifBold;
1504 int pifUnderline;
1505
1506 DEVMODE *mem;
1507 DEVNAMES *devname;
1508 int i;
1509
1510 bUserAbort = &(psettings->user_abort);
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02001511 vim_memset(&prt_dlg, 0, sizeof(PRINTDLG));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512 prt_dlg.lStructSize = sizeof(PRINTDLG);
1513#ifndef FEAT_GUI
1514 GetConsoleHwnd(); /* get value of s_hwnd */
1515#endif
1516 prt_dlg.hwndOwner = s_hwnd;
1517 prt_dlg.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
1518 if (!forceit)
1519 {
1520 prt_dlg.hDevMode = stored_dm;
1521 prt_dlg.hDevNames = stored_devn;
1522 prt_dlg.lCustData = stored_nCopies; // work around bug in print dialog
1523#ifndef FEAT_GUI
1524 /*
1525 * Use hook to prevent console window being sent to back
1526 */
1527 prt_dlg.lpfnPrintHook = PrintHookProc;
1528 prt_dlg.Flags |= PD_ENABLEPRINTHOOK;
1529#endif
1530 prt_dlg.Flags |= stored_nFlags;
1531 }
1532
1533 /*
1534 * If bang present, return default printer setup with no dialog
1535 * never show dialog if we are running over telnet
1536 */
1537 if (forceit
1538#ifndef FEAT_GUI
1539 || !term_console
1540#endif
1541 )
1542 {
1543 prt_dlg.Flags |= PD_RETURNDEFAULT;
1544#ifdef WIN3264
1545 /*
1546 * MSDN suggests setting the first parameter to WINSPOOL for
1547 * NT, but NULL appears to work just as well.
1548 */
1549 if (*p_pdev != NUL)
1550 prt_dlg.hDC = CreateDC(NULL, p_pdev, NULL, NULL);
1551 else
1552#endif
1553 {
1554 prt_dlg.Flags |= PD_RETURNDEFAULT;
1555 if (PrintDlg(&prt_dlg) == 0)
1556 goto init_fail_dlg;
1557 }
1558 }
1559 else if (PrintDlg(&prt_dlg) == 0)
1560 goto init_fail_dlg;
1561 else
1562 {
1563 /*
1564 * keep the previous driver context
1565 */
1566 stored_dm = prt_dlg.hDevMode;
1567 stored_devn = prt_dlg.hDevNames;
1568 stored_nFlags = prt_dlg.Flags;
1569 stored_nCopies = prt_dlg.nCopies;
1570 }
1571
1572 if (prt_dlg.hDC == NULL)
1573 {
1574 EMSG(_("E237: Printer selection failed"));
1575 mch_print_cleanup();
1576 return FALSE;
1577 }
1578
1579 /* Not all printer drivers report the support of color (or grey) in the
1580 * same way. Let's set has_color if there appears to be some way to print
1581 * more than B&W. */
1582 i = GetDeviceCaps(prt_dlg.hDC, NUMCOLORS);
1583 psettings->has_color = (GetDeviceCaps(prt_dlg.hDC, BITSPIXEL) > 1
1584 || GetDeviceCaps(prt_dlg.hDC, PLANES) > 1
1585 || i > 2 || i == -1);
1586
1587 /* Ensure all font styles are baseline aligned */
1588 SetTextAlign(prt_dlg.hDC, TA_BASELINE|TA_LEFT);
1589
1590 /*
1591 * On some windows systems the nCopies parameter is not
1592 * passed back correctly. It must be retrieved from the
1593 * hDevMode struct.
1594 */
1595 mem = (DEVMODE *)GlobalLock(prt_dlg.hDevMode);
1596 if (mem != NULL)
1597 {
1598#ifdef WIN3264
1599 if (mem->dmCopies != 1)
1600 stored_nCopies = mem->dmCopies;
1601#endif
1602 if ((mem->dmFields & DM_DUPLEX) && (mem->dmDuplex & ~DMDUP_SIMPLEX))
1603 psettings->duplex = TRUE;
1604 if ((mem->dmFields & DM_COLOR) && (mem->dmColor & DMCOLOR_COLOR))
1605 psettings->has_color = TRUE;
1606 }
1607 GlobalUnlock(prt_dlg.hDevMode);
1608
1609 devname = (DEVNAMES *)GlobalLock(prt_dlg.hDevNames);
1610 if (devname != 0)
1611 {
1612 char_u *printer_name = (char_u *)devname + devname->wDeviceOffset;
1613 char_u *port_name = (char_u *)devname +devname->wOutputOffset;
1614 char_u *text = _("to %s on %s");
1615
Bram Moolenaare6a91fd2008-07-24 18:51:11 +00001616 prt_name = alloc((unsigned)(STRLEN(printer_name) + STRLEN(port_name)
1617 + STRLEN(text)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001618 if (prt_name != NULL)
1619 wsprintf(prt_name, text, printer_name, port_name);
1620 }
1621 GlobalUnlock(prt_dlg.hDevNames);
1622
1623 /*
1624 * Initialise the font according to 'printfont'
1625 */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02001626 vim_memset(&fLogFont, 0, sizeof(fLogFont));
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00001627 if (get_logfont(&fLogFont, p_pfn, prt_dlg.hDC, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001628 {
1629 EMSG2(_("E613: Unknown printer font: %s"), p_pfn);
1630 mch_print_cleanup();
1631 return FALSE;
1632 }
1633
1634 for (pifBold = 0; pifBold <= 1; pifBold++)
1635 for (pifItalic = 0; pifItalic <= 1; pifItalic++)
1636 for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
1637 {
1638 fLogFont.lfWeight = boldface[pifBold];
1639 fLogFont.lfItalic = pifItalic;
1640 fLogFont.lfUnderline = pifUnderline;
1641 prt_font_handles[pifBold][pifItalic][pifUnderline]
1642 = CreateFontIndirect(&fLogFont);
1643 }
1644
1645 SetBkMode(prt_dlg.hDC, OPAQUE);
1646 SelectObject(prt_dlg.hDC, prt_font_handles[0][0][0]);
1647
1648 /*
1649 * Fill in the settings struct
1650 */
1651 psettings->chars_per_line = prt_get_cpl();
1652 psettings->lines_per_page = prt_get_lpp();
1653 psettings->n_collated_copies = (prt_dlg.Flags & PD_COLLATE)
1654 ? prt_dlg.nCopies : 1;
1655 psettings->n_uncollated_copies = (prt_dlg.Flags & PD_COLLATE)
1656 ? 1 : prt_dlg.nCopies;
1657
1658 if (psettings->n_collated_copies == 0)
1659 psettings->n_collated_copies = 1;
1660
1661 if (psettings->n_uncollated_copies == 0)
1662 psettings->n_uncollated_copies = 1;
1663
1664 psettings->jobname = jobname;
1665
1666 return TRUE;
1667
1668init_fail_dlg:
1669 {
1670 DWORD err = CommDlgExtendedError();
1671
1672 if (err)
1673 {
1674#ifdef WIN16
1675 char buf[20];
1676
1677 sprintf(buf, "%ld", err);
1678 EMSG2(_("E238: Print error: %s"), buf);
1679#else
1680 char_u *buf;
1681
1682 /* I suspect FormatMessage() doesn't work for values returned by
1683 * CommDlgExtendedError(). What does? */
1684 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
1685 FORMAT_MESSAGE_FROM_SYSTEM |
1686 FORMAT_MESSAGE_IGNORE_INSERTS,
1687 NULL, err, 0, (LPTSTR)(&buf), 0, NULL);
1688 EMSG2(_("E238: Print error: %s"),
1689 buf == NULL ? (char_u *)_("Unknown") : buf);
1690 LocalFree((LPVOID)(buf));
1691#endif
1692 }
1693 else
1694 msg_clr_eos(); /* Maybe canceled */
1695
1696 mch_print_cleanup();
1697 return FALSE;
1698 }
1699}
1700
1701
1702 int
1703mch_print_begin(prt_settings_T *psettings)
1704{
1705 int ret;
1706 static DOCINFO di;
1707 char szBuffer[300];
1708
1709 hDlgPrint = CreateDialog(GetModuleHandle(NULL), TEXT("PrintDlgBox"),
1710 prt_dlg.hwndOwner, PrintDlgProc);
1711#ifdef WIN16
1712 Escape(prt_dlg.hDC, SETABORTPROC, 0, (LPSTR)AbortProc, NULL);
1713#else
1714 SetAbortProc(prt_dlg.hDC, AbortProc);
1715#endif
1716 wsprintf(szBuffer, _("Printing '%s'"), gettail(psettings->jobname));
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001717 vimSetDlgItemText(hDlgPrint, IDC_PRINTTEXT1, (LPSTR)szBuffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02001719 vim_memset(&di, 0, sizeof(DOCINFO));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 di.cbSize = sizeof(DOCINFO);
1721 di.lpszDocName = psettings->jobname;
1722 ret = StartDoc(prt_dlg.hDC, &di);
1723
1724#ifdef FEAT_GUI
1725 /* Give focus back to main window (when using MDI). */
1726 SetFocus(s_hwnd);
1727#endif
1728
1729 return (ret > 0);
1730}
1731
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00001732/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00001733 void
1734mch_print_end(prt_settings_T *psettings)
1735{
1736 EndDoc(prt_dlg.hDC);
1737 if (!*bUserAbort)
1738 SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
1739}
1740
1741 int
1742mch_print_end_page(void)
1743{
1744 return (EndPage(prt_dlg.hDC) > 0);
1745}
1746
1747 int
1748mch_print_begin_page(char_u *msg)
1749{
1750 if (msg != NULL)
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001751 vimSetDlgItemText(hDlgPrint, IDC_PROGRESS, (LPSTR)msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752 return (StartPage(prt_dlg.hDC) > 0);
1753}
1754
1755 int
1756mch_print_blank_page(void)
1757{
1758 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
1759}
1760
1761static int prt_pos_x = 0;
1762static int prt_pos_y = 0;
1763
1764 void
1765mch_print_start_line(margin, page_line)
1766 int margin;
1767 int page_line;
1768{
1769 if (margin)
1770 prt_pos_x = -prt_number_width;
1771 else
1772 prt_pos_x = 0;
1773 prt_pos_y = page_line * prt_line_height
1774 + prt_tm.tmAscent + prt_tm.tmExternalLeading;
1775}
1776
1777 int
1778mch_print_text_out(char_u *p, int len)
1779{
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001780#if defined(FEAT_PROPORTIONAL_FONTS) || (defined(FEAT_MBYTE) && !defined(WIN16))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781 SIZE sz;
1782#endif
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001783#if defined(FEAT_MBYTE) && !defined(WIN16)
1784 WCHAR *wp = NULL;
1785 int wlen = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786
Bram Moolenaar5246cd72013-06-16 16:41:47 +02001787 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
1788 {
1789 wp = enc_to_utf16(p, &wlen);
1790 }
1791 if (wp != NULL)
1792 {
1793 int ret = FALSE;
1794
1795 TextOutW(prt_dlg.hDC, prt_pos_x + prt_left_margin,
1796 prt_pos_y + prt_top_margin, wp, wlen);
1797 GetTextExtentPoint32W(prt_dlg.hDC, wp, wlen, &sz);
1798 vim_free(wp);
1799 prt_pos_x += (sz.cx - prt_tm.tmOverhang);
1800 /* This is wrong when printing spaces for a TAB. */
1801 if (p[len] != NUL)
1802 {
1803 wlen = MB_PTR2LEN(p + len);
1804 wp = enc_to_utf16(p + len, &wlen);
1805 if (wp != NULL)
1806 {
1807 GetTextExtentPoint32W(prt_dlg.hDC, wp, 1, &sz);
1808 ret = (prt_pos_x + prt_left_margin + sz.cx > prt_right_margin);
1809 vim_free(wp);
1810 }
1811 }
1812 return ret;
1813 }
1814#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815 TextOut(prt_dlg.hDC, prt_pos_x + prt_left_margin,
1816 prt_pos_y + prt_top_margin, p, len);
1817#ifndef FEAT_PROPORTIONAL_FONTS
1818 prt_pos_x += len * prt_tm.tmAveCharWidth;
1819 return (prt_pos_x + prt_left_margin + prt_tm.tmAveCharWidth
1820 + prt_tm.tmOverhang > prt_right_margin);
1821#else
1822# ifdef WIN16
1823 GetTextExtentPoint(prt_dlg.hDC, p, len, &sz);
1824# else
1825 GetTextExtentPoint32(prt_dlg.hDC, p, len, &sz);
1826# endif
1827 prt_pos_x += (sz.cx - prt_tm.tmOverhang);
1828 /* This is wrong when printing spaces for a TAB. */
1829 if (p[len] == NUL)
1830 return FALSE;
1831# ifdef WIN16
1832 GetTextExtentPoint(prt_dlg.hDC, p + len, 1, &sz);
1833# else
1834 GetTextExtentPoint32(prt_dlg.hDC, p + len, 1, &sz);
1835# endif
1836 return (prt_pos_x + prt_left_margin + sz.cx > prt_right_margin);
1837#endif
1838}
1839
1840 void
1841mch_print_set_font(int iBold, int iItalic, int iUnderline)
1842{
1843 SelectObject(prt_dlg.hDC, prt_font_handles[iBold][iItalic][iUnderline]);
1844}
1845
1846 void
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001847mch_print_set_bg(long_u bgcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001848{
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001849 SetBkColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC,
1850 swap_me((COLORREF)bgcol)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851 /*
1852 * With a white background we can draw characters transparent, which is
1853 * good for italic characters that overlap to the next char cell.
1854 */
1855 if (bgcol == 0xffffffUL)
1856 SetBkMode(prt_dlg.hDC, TRANSPARENT);
1857 else
1858 SetBkMode(prt_dlg.hDC, OPAQUE);
1859}
1860
1861 void
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001862mch_print_set_fg(long_u fgcol)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863{
Bram Moolenaar551dbcc2006-04-25 22:13:59 +00001864 SetTextColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC,
1865 swap_me((COLORREF)fgcol)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866}
1867
1868#endif /*FEAT_PRINTER && !FEAT_POSTSCRIPT*/
1869
Bram Moolenaar58d98232005-07-23 22:25:46 +00001870
1871
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872#if defined(FEAT_SHORTCUT) || defined(PROTO)
Bram Moolenaar82881492012-11-20 16:53:39 +01001873# ifndef PROTO
1874# include <shlobj.h>
1875# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876
1877/*
1878 * When "fname" is the name of a shortcut (*.lnk) resolve the file it points
1879 * to and return that name in allocated memory.
1880 * Otherwise NULL is returned.
1881 */
1882 char_u *
1883mch_resolve_shortcut(char_u *fname)
1884{
1885 HRESULT hr;
1886 IShellLink *psl = NULL;
1887 IPersistFile *ppf = NULL;
1888 OLECHAR wsz[MAX_PATH];
1889 WIN32_FIND_DATA ffd; // we get those free of charge
Bram Moolenaar604729e2013-08-30 16:44:19 +02001890 CHAR buf[MAX_PATH]; // could have simply reused 'wsz'...
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891 char_u *rfname = NULL;
1892 int len;
Bram Moolenaar604729e2013-08-30 16:44:19 +02001893# ifdef FEAT_MBYTE
1894 IShellLinkW *pslw = NULL;
1895 WIN32_FIND_DATAW ffdw; // we get those free of charge
1896# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001897
1898 /* Check if the file name ends in ".lnk". Avoid calling
1899 * CoCreateInstance(), it's quite slow. */
1900 if (fname == NULL)
1901 return rfname;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001902 len = (int)STRLEN(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903 if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0)
1904 return rfname;
1905
1906 CoInitialize(NULL);
1907
Bram Moolenaar604729e2013-08-30 16:44:19 +02001908# ifdef FEAT_MBYTE
1909 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
1910 {
1911 // create a link manager object and request its interface
1912 hr = CoCreateInstance(
1913 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1914 &IID_IShellLinkW, (void**)&pslw);
1915 if (hr == S_OK)
1916 {
1917 WCHAR *p = enc_to_utf16(fname, NULL);
1918
1919 if (p != NULL)
1920 {
1921 // Get a pointer to the IPersistFile interface.
1922 hr = pslw->lpVtbl->QueryInterface(
1923 pslw, &IID_IPersistFile, (void**)&ppf);
1924 if (hr != S_OK)
1925 goto shortcut_errorw;
1926
1927 // "load" the name and resolve the link
1928 hr = ppf->lpVtbl->Load(ppf, p, STGM_READ);
1929 if (hr != S_OK)
1930 goto shortcut_errorw;
1931# if 0 // This makes Vim wait a long time if the target does not exist.
1932 hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI);
1933 if (hr != S_OK)
1934 goto shortcut_errorw;
1935# endif
1936
1937 // Get the path to the link target.
1938 ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR));
1939 hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0);
1940 if (hr == S_OK && wsz[0] != NUL)
1941 rfname = utf16_to_enc(wsz, NULL);
1942
1943shortcut_errorw:
1944 vim_free(p);
1945 if (hr == S_OK)
1946 goto shortcut_end;
1947 }
1948 }
1949 /* Retry with non-wide function (for Windows 98). */
1950 }
1951# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952 // create a link manager object and request its interface
1953 hr = CoCreateInstance(
1954 &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1955 &IID_IShellLink, (void**)&psl);
1956 if (hr != S_OK)
Bram Moolenaar604729e2013-08-30 16:44:19 +02001957 goto shortcut_end;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958
1959 // Get a pointer to the IPersistFile interface.
1960 hr = psl->lpVtbl->QueryInterface(
1961 psl, &IID_IPersistFile, (void**)&ppf);
1962 if (hr != S_OK)
Bram Moolenaar604729e2013-08-30 16:44:19 +02001963 goto shortcut_end;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964
1965 // full path string must be in Unicode.
1966 MultiByteToWideChar(CP_ACP, 0, fname, -1, wsz, MAX_PATH);
1967
Bram Moolenaarff1d0d42007-05-10 17:24:16 +00001968 // "load" the name and resolve the link
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969 hr = ppf->lpVtbl->Load(ppf, wsz, STGM_READ);
1970 if (hr != S_OK)
Bram Moolenaar604729e2013-08-30 16:44:19 +02001971 goto shortcut_end;
1972# if 0 // This makes Vim wait a long time if the target doesn't exist.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 hr = psl->lpVtbl->Resolve(psl, NULL, SLR_NO_UI);
1974 if (hr != S_OK)
Bram Moolenaar604729e2013-08-30 16:44:19 +02001975 goto shortcut_end;
1976# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977
1978 // Get the path to the link target.
1979 ZeroMemory(buf, MAX_PATH);
1980 hr = psl->lpVtbl->GetPath(psl, buf, MAX_PATH, &ffd, 0);
1981 if (hr == S_OK && buf[0] != NUL)
1982 rfname = vim_strsave(buf);
1983
Bram Moolenaar604729e2013-08-30 16:44:19 +02001984shortcut_end:
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 // Release all interface pointers (both belong to the same object)
1986 if (ppf != NULL)
1987 ppf->lpVtbl->Release(ppf);
1988 if (psl != NULL)
1989 psl->lpVtbl->Release(psl);
Bram Moolenaar604729e2013-08-30 16:44:19 +02001990# ifdef FEAT_MBYTE
1991 if (pslw != NULL)
1992 pslw->lpVtbl->Release(pslw);
1993# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994
1995 CoUninitialize();
1996 return rfname;
1997}
1998#endif
1999
2000#if (defined(FEAT_EVAL) && !defined(FEAT_GUI)) || defined(PROTO)
2001/*
2002 * Bring ourselves to the foreground. Does work if the OS doesn't allow it.
2003 */
2004 void
2005win32_set_foreground()
2006{
2007# ifndef FEAT_GUI
2008 GetConsoleHwnd(); /* get value of s_hwnd */
2009# endif
2010 if (s_hwnd != 0)
2011 SetForegroundWindow(s_hwnd);
2012}
2013#endif
2014
2015#if defined(FEAT_CLIENTSERVER) || defined(PROTO)
2016/*
2017 * Client-server code for Vim
2018 *
2019 * Originally written by Paul Moore
2020 */
2021
2022/* In order to handle inter-process messages, we need to have a window. But
2023 * the functions in this module can be called before the main GUI window is
2024 * created (and may also be called in the console version, where there is no
2025 * GUI window at all).
2026 *
2027 * So we create a hidden window, and arrange to destroy it on exit.
2028 */
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02002029HWND message_window = 0; /* window that's handling messages */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030
2031#define VIM_CLASSNAME "VIM_MESSAGES"
2032#define VIM_CLASSNAME_LEN (sizeof(VIM_CLASSNAME) - 1)
2033
2034/* Communication is via WM_COPYDATA messages. The message type is send in
2035 * the dwData parameter. Types are defined here. */
2036#define COPYDATA_KEYS 0
2037#define COPYDATA_REPLY 1
2038#define COPYDATA_EXPR 10
2039#define COPYDATA_RESULT 11
2040#define COPYDATA_ERROR_RESULT 12
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002041#define COPYDATA_ENCODING 20
Bram Moolenaar071d4272004-06-13 20:20:40 +00002042
2043/* This is a structure containing a server HWND and its name. */
2044struct server_id
2045{
2046 HWND hwnd;
2047 char_u *name;
2048};
2049
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002050/* Last received 'encoding' that the client uses. */
2051static char_u *client_enc = NULL;
2052
2053/*
2054 * Tell the other side what encoding we are using.
2055 * Errors are ignored.
2056 */
2057 static void
2058serverSendEnc(HWND target)
2059{
2060 COPYDATASTRUCT data;
2061
2062 data.dwData = COPYDATA_ENCODING;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002063#ifdef FEAT_MBYTE
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002064 data.cbData = (DWORD)STRLEN(p_enc) + 1;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002065 data.lpData = p_enc;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002066#else
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02002067 data.cbData = (DWORD)STRLEN("latin1") + 1;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002068 data.lpData = "latin1";
2069#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002070 (void)SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
2071 (LPARAM)(&data));
2072}
2073
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074/*
2075 * Clean up on exit. This destroys the hidden message window.
2076 */
2077 static void
2078#ifdef __BORLANDC__
2079 _RTLENTRYF
2080#endif
2081CleanUpMessaging(void)
2082{
2083 if (message_window != 0)
2084 {
2085 DestroyWindow(message_window);
2086 message_window = 0;
2087 }
2088}
2089
2090static int save_reply(HWND server, char_u *reply, int expr);
2091
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02002092/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002093 * The window procedure for the hidden message window.
2094 * It handles callback messages and notifications from servers.
2095 * In order to process these messages, it is necessary to run a
2096 * message loop. Code which may run before the main message loop
2097 * is started (in the GUI) is careful to pump messages when it needs
2098 * to. Features which require message delivery during normal use will
2099 * not work in the console version - this basically means those
2100 * features which allow Vim to act as a server, rather than a client.
2101 */
2102 static LRESULT CALLBACK
2103Messaging_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2104{
2105 if (msg == WM_COPYDATA)
2106 {
2107 /* This is a message from another Vim. The dwData member of the
2108 * COPYDATASTRUCT determines the type of message:
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002109 * COPYDATA_ENCODING:
2110 * The encoding that the client uses. Following messages will
2111 * use this encoding, convert if needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 * COPYDATA_KEYS:
2113 * A key sequence. We are a server, and a client wants these keys
2114 * adding to the input queue.
2115 * COPYDATA_REPLY:
2116 * A reply. We are a client, and a server has sent this message
2117 * in response to a request. (server2client())
2118 * COPYDATA_EXPR:
2119 * An expression. We are a server, and a client wants us to
2120 * evaluate this expression.
2121 * COPYDATA_RESULT:
2122 * A reply. We are a client, and a server has sent this message
2123 * in response to a COPYDATA_EXPR.
2124 * COPYDATA_ERROR_RESULT:
2125 * A reply. We are a client, and a server has sent this message
2126 * in response to a COPYDATA_EXPR that failed to evaluate.
2127 */
2128 COPYDATASTRUCT *data = (COPYDATASTRUCT*)lParam;
2129 HWND sender = (HWND)wParam;
2130 COPYDATASTRUCT reply;
2131 char_u *res;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 int retval;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002133 char_u *str;
2134 char_u *tofree;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002135
2136 switch (data->dwData)
2137 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002138 case COPYDATA_ENCODING:
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00002139# ifdef FEAT_MBYTE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002140 /* Remember the encoding that the client uses. */
2141 vim_free(client_enc);
2142 client_enc = enc_canonize((char_u *)data->lpData);
Bram Moolenaarbc7aa852005-03-06 23:38:09 +00002143# endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002144 return 1;
2145
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146 case COPYDATA_KEYS:
2147 /* Remember who sent this, for <client> */
2148 clientWindow = sender;
2149
2150 /* Add the received keys to the input buffer. The loop waiting
2151 * for the user to do something should check the input buffer. */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002152 str = serverConvert(client_enc, (char_u *)data->lpData, &tofree);
2153 server_to_input_buf(str);
2154 vim_free(tofree);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155
2156# ifdef FEAT_GUI
2157 /* Wake up the main GUI loop. */
2158 if (s_hwnd != 0)
2159 PostMessage(s_hwnd, WM_NULL, 0, 0);
2160# endif
2161 return 1;
2162
2163 case COPYDATA_EXPR:
2164 /* Remember who sent this, for <client> */
2165 clientWindow = sender;
2166
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002167 str = serverConvert(client_enc, (char_u *)data->lpData, &tofree);
2168 res = eval_client_expr_to_string(str);
2169 vim_free(tofree);
2170
Bram Moolenaar071d4272004-06-13 20:20:40 +00002171 if (res == NULL)
2172 {
2173 res = vim_strsave(_(e_invexprmsg));
2174 reply.dwData = COPYDATA_ERROR_RESULT;
2175 }
2176 else
2177 reply.dwData = COPYDATA_RESULT;
2178 reply.lpData = res;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002179 reply.cbData = (DWORD)STRLEN(res) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002180
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002181 serverSendEnc(sender);
Bram Moolenaar5246cd72013-06-16 16:41:47 +02002182 retval = (int)SendMessage(sender, WM_COPYDATA,
2183 (WPARAM)message_window, (LPARAM)(&reply));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002184 vim_free(res);
2185 return retval;
2186
2187 case COPYDATA_REPLY:
2188 case COPYDATA_RESULT:
2189 case COPYDATA_ERROR_RESULT:
2190 if (data->lpData != NULL)
2191 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002192 str = serverConvert(client_enc, (char_u *)data->lpData,
2193 &tofree);
2194 if (tofree == NULL)
2195 str = vim_strsave(str);
2196 if (save_reply(sender, str,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197 (data->dwData == COPYDATA_REPLY ? 0 :
2198 (data->dwData == COPYDATA_RESULT ? 1 :
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002199 2))) == FAIL)
2200 vim_free(str);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201#ifdef FEAT_AUTOCMD
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002202 else if (data->dwData == COPYDATA_REPLY)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 {
Bram Moolenaar427d51c2013-06-16 16:01:25 +02002204 char_u winstr[30];
2205
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002206 sprintf((char *)winstr, PRINTF_HEX_LONG_U, (long_u)sender);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002207 apply_autocmds(EVENT_REMOTEREPLY, winstr, str,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 TRUE, curbuf);
2209 }
2210#endif
2211 }
2212 return 1;
2213 }
2214
2215 return 0;
2216 }
2217
2218 else if (msg == WM_ACTIVATE && wParam == WA_ACTIVE)
2219 {
2220 /* When the message window is activated (brought to the foreground),
2221 * this actually applies to the text window. */
2222#ifndef FEAT_GUI
2223 GetConsoleHwnd(); /* get value of s_hwnd */
2224#endif
2225 if (s_hwnd != 0)
2226 {
2227 SetForegroundWindow(s_hwnd);
2228 return 0;
2229 }
2230 }
2231
2232 return DefWindowProc(hwnd, msg, wParam, lParam);
2233}
2234
2235/*
2236 * Initialise the message handling process. This involves creating a window
2237 * to handle messages - the window will not be visible.
2238 */
2239 void
2240serverInitMessaging(void)
2241{
2242 WNDCLASS wndclass;
2243 HINSTANCE s_hinst;
2244
2245 /* Clean up on exit */
2246 atexit(CleanUpMessaging);
2247
2248 /* Register a window class - we only really care
2249 * about the window procedure
2250 */
2251 s_hinst = (HINSTANCE)GetModuleHandle(0);
2252 wndclass.style = 0;
2253 wndclass.lpfnWndProc = Messaging_WndProc;
2254 wndclass.cbClsExtra = 0;
2255 wndclass.cbWndExtra = 0;
2256 wndclass.hInstance = s_hinst;
2257 wndclass.hIcon = NULL;
2258 wndclass.hCursor = NULL;
2259 wndclass.hbrBackground = NULL;
2260 wndclass.lpszMenuName = NULL;
2261 wndclass.lpszClassName = VIM_CLASSNAME;
2262 RegisterClass(&wndclass);
2263
2264 /* Create the message window. It will be hidden, so the details don't
2265 * matter. Don't use WS_OVERLAPPEDWINDOW, it will make a shortcut remove
2266 * focus from gvim. */
2267 message_window = CreateWindow(VIM_CLASSNAME, "",
2268 WS_POPUPWINDOW | WS_CAPTION,
2269 CW_USEDEFAULT, CW_USEDEFAULT,
2270 100, 100, NULL, NULL,
2271 s_hinst, NULL);
2272}
2273
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002274/* Used by serverSendToVim() to find an alternate server name. */
2275static char_u *altname_buf_ptr = NULL;
2276
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277/*
2278 * Get the title of the window "hwnd", which is the Vim server name, in
2279 * "name[namelen]" and return the length.
2280 * Returns zero if window "hwnd" is not a Vim server.
2281 */
2282 static int
2283getVimServerName(HWND hwnd, char *name, int namelen)
2284{
2285 int len;
2286 char buffer[VIM_CLASSNAME_LEN + 1];
2287
2288 /* Ignore windows which aren't Vim message windows */
2289 len = GetClassName(hwnd, buffer, sizeof(buffer));
2290 if (len != VIM_CLASSNAME_LEN || STRCMP(buffer, VIM_CLASSNAME) != 0)
2291 return 0;
2292
2293 /* Get the title of the window */
2294 return GetWindowText(hwnd, name, namelen);
2295}
2296
2297 static BOOL CALLBACK
2298enumWindowsGetServer(HWND hwnd, LPARAM lparam)
2299{
2300 struct server_id *id = (struct server_id *)lparam;
2301 char server[MAX_PATH];
2302
2303 /* Get the title of the window */
2304 if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2305 return TRUE;
2306
2307 /* If this is the server we're looking for, return its HWND */
2308 if (STRICMP(server, id->name) == 0)
2309 {
2310 id->hwnd = hwnd;
2311 return FALSE;
2312 }
2313
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002314 /* If we are looking for an alternate server, remember this name. */
2315 if (altname_buf_ptr != NULL
2316 && STRNICMP(server, id->name, STRLEN(id->name)) == 0
2317 && vim_isdigit(server[STRLEN(id->name)]))
2318 {
2319 STRCPY(altname_buf_ptr, server);
2320 altname_buf_ptr = NULL; /* don't use another name */
2321 }
2322
Bram Moolenaar071d4272004-06-13 20:20:40 +00002323 /* Otherwise, keep looking */
2324 return TRUE;
2325}
2326
2327 static BOOL CALLBACK
2328enumWindowsGetNames(HWND hwnd, LPARAM lparam)
2329{
2330 garray_T *ga = (garray_T *)lparam;
2331 char server[MAX_PATH];
2332
2333 /* Get the title of the window */
2334 if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2335 return TRUE;
2336
2337 /* Add the name to the list */
2338 ga_concat(ga, server);
2339 ga_concat(ga, "\n");
2340 return TRUE;
2341}
2342
2343 static HWND
2344findServer(char_u *name)
2345{
2346 struct server_id id;
2347
2348 id.name = name;
2349 id.hwnd = 0;
2350
2351 EnumWindows(enumWindowsGetServer, (LPARAM)(&id));
2352
2353 return id.hwnd;
2354}
2355
2356 void
2357serverSetName(char_u *name)
2358{
2359 char_u *ok_name;
2360 HWND hwnd = 0;
2361 int i = 0;
2362 char_u *p;
2363
2364 /* Leave enough space for a 9-digit suffix to ensure uniqueness! */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002365 ok_name = alloc((unsigned)STRLEN(name) + 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366
2367 STRCPY(ok_name, name);
2368 p = ok_name + STRLEN(name);
2369
2370 for (;;)
2371 {
2372 /* This is inefficient - we're doing an EnumWindows loop for each
2373 * possible name. It would be better to grab all names in one go,
2374 * and scan the list each time...
2375 */
2376 hwnd = findServer(ok_name);
2377 if (hwnd == 0)
2378 break;
2379
2380 ++i;
2381 if (i >= 1000)
2382 break;
2383
2384 sprintf((char *)p, "%d", i);
2385 }
2386
2387 if (hwnd != 0)
2388 vim_free(ok_name);
2389 else
2390 {
2391 /* Remember the name */
2392 serverName = ok_name;
2393#ifdef FEAT_TITLE
2394 need_maketitle = TRUE; /* update Vim window title later */
2395#endif
2396
2397 /* Update the message window title */
2398 SetWindowText(message_window, ok_name);
2399
2400#ifdef FEAT_EVAL
2401 /* Set the servername variable */
2402 set_vim_var_string(VV_SEND_SERVER, serverName, -1);
2403#endif
2404 }
2405}
2406
2407 char_u *
2408serverGetVimNames(void)
2409{
2410 garray_T ga;
2411
2412 ga_init2(&ga, 1, 100);
2413
2414 EnumWindows(enumWindowsGetNames, (LPARAM)(&ga));
Bram Moolenaar269ec652004-07-29 08:43:53 +00002415 ga_append(&ga, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416
2417 return ga.ga_data;
2418}
2419
2420 int
2421serverSendReply(name, reply)
2422 char_u *name; /* Where to send. */
2423 char_u *reply; /* What to send. */
2424{
2425 HWND target;
2426 COPYDATASTRUCT data;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002427 long_u n = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002428
2429 /* The "name" argument is a magic cookie obtained from expand("<client>").
2430 * It should be of the form 0xXXXXX - i.e. a C hex literal, which is the
2431 * value of the client's message window HWND.
2432 */
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002433 sscanf((char *)name, SCANF_HEX_LONG_U, &n);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 if (n == 0)
2435 return -1;
2436
2437 target = (HWND)n;
2438 if (!IsWindow(target))
2439 return -1;
2440
2441 data.dwData = COPYDATA_REPLY;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002442 data.cbData = (DWORD)STRLEN(reply) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 data.lpData = reply;
2444
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002445 serverSendEnc(target);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446 if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
2447 (LPARAM)(&data)))
2448 return 0;
2449
2450 return -1;
2451}
2452
2453 int
2454serverSendToVim(name, cmd, result, ptarget, asExpr, silent)
2455 char_u *name; /* Where to send. */
2456 char_u *cmd; /* What to send. */
2457 char_u **result; /* Result of eval'ed expression */
2458 void *ptarget; /* HWND of server */
2459 int asExpr; /* Expression or keys? */
2460 int silent; /* don't complain about no server */
2461{
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002462 HWND target;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463 COPYDATASTRUCT data;
2464 char_u *retval = NULL;
2465 int retcode = 0;
Bram Moolenaarf193fff2006-04-27 00:02:13 +00002466 char_u altname_buf[MAX_PATH];
2467
2468 /* If the server name does not end in a digit then we look for an
2469 * alternate name. e.g. when "name" is GVIM the we may find GVIM2. */
2470 if (STRLEN(name) > 1 && !vim_isdigit(name[STRLEN(name) - 1]))
2471 altname_buf_ptr = altname_buf;
2472 altname_buf[0] = NUL;
2473 target = findServer(name);
2474 altname_buf_ptr = NULL;
2475 if (target == 0 && altname_buf[0] != NUL)
2476 /* Use another server name we found. */
2477 target = findServer(altname_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002478
2479 if (target == 0)
2480 {
2481 if (!silent)
2482 EMSG2(_(e_noserver), name);
2483 return -1;
2484 }
2485
2486 if (ptarget)
2487 *(HWND *)ptarget = target;
2488
2489 data.dwData = asExpr ? COPYDATA_EXPR : COPYDATA_KEYS;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00002490 data.cbData = (DWORD)STRLEN(cmd) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491 data.lpData = cmd;
2492
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002493 serverSendEnc(target);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494 if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
2495 (LPARAM)(&data)) == 0)
2496 return -1;
2497
2498 if (asExpr)
2499 retval = serverGetReply(target, &retcode, TRUE, TRUE);
2500
2501 if (result == NULL)
2502 vim_free(retval);
2503 else
2504 *result = retval; /* Caller assumes responsibility for freeing */
2505
2506 return retcode;
2507}
2508
2509/*
2510 * Bring the server to the foreground.
2511 */
2512 void
2513serverForeground(name)
2514 char_u *name;
2515{
2516 HWND target = findServer(name);
2517
2518 if (target != 0)
2519 SetForegroundWindow(target);
2520}
2521
2522/* Replies from server need to be stored until the client picks them up via
2523 * remote_read(). So we maintain a list of server-id/reply pairs.
2524 * Note that there could be multiple replies from one server pending if the
2525 * client is slow picking them up.
2526 * We just store the replies in a simple list. When we remove an entry, we
2527 * move list entries down to fill the gap.
2528 * The server ID is simply the HWND.
2529 */
2530typedef struct
2531{
2532 HWND server; /* server window */
2533 char_u *reply; /* reply string */
2534 int expr_result; /* 0 for REPLY, 1 for RESULT 2 for error */
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002535} reply_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536
2537static garray_T reply_list = {0, 0, sizeof(reply_T), 5, 0};
2538
2539#define REPLY_ITEM(i) ((reply_T *)(reply_list.ga_data) + (i))
2540#define REPLY_COUNT (reply_list.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541
2542/* Flag which is used to wait for a reply */
2543static int reply_received = 0;
2544
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002545/*
2546 * Store a reply. "reply" must be allocated memory (or NULL).
2547 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002548 static int
2549save_reply(HWND server, char_u *reply, int expr)
2550{
2551 reply_T *rep;
2552
2553 if (ga_grow(&reply_list, 1) == FAIL)
2554 return FAIL;
2555
2556 rep = REPLY_ITEM(REPLY_COUNT);
2557 rep->server = server;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002558 rep->reply = reply;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 rep->expr_result = expr;
2560 if (rep->reply == NULL)
2561 return FAIL;
2562
2563 ++REPLY_COUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564 reply_received = 1;
2565 return OK;
2566}
2567
2568/*
2569 * Get a reply from server "server".
2570 * When "expr_res" is non NULL, get the result of an expression, otherwise a
2571 * server2client() message.
2572 * When non NULL, point to return code. 0 => OK, -1 => ERROR
2573 * If "remove" is TRUE, consume the message, the caller must free it then.
2574 * if "wait" is TRUE block until a message arrives (or the server exits).
2575 */
2576 char_u *
2577serverGetReply(HWND server, int *expr_res, int remove, int wait)
2578{
2579 int i;
2580 char_u *reply;
2581 reply_T *rep;
2582
2583 /* When waiting, loop until the message waiting for is received. */
2584 for (;;)
2585 {
2586 /* Reset this here, in case a message arrives while we are going
2587 * through the already received messages. */
2588 reply_received = 0;
2589
2590 for (i = 0; i < REPLY_COUNT; ++i)
2591 {
2592 rep = REPLY_ITEM(i);
2593 if (rep->server == server
2594 && ((rep->expr_result != 0) == (expr_res != NULL)))
2595 {
2596 /* Save the values we've found for later */
2597 reply = rep->reply;
2598 if (expr_res != NULL)
2599 *expr_res = rep->expr_result == 1 ? 0 : -1;
2600
2601 if (remove)
2602 {
2603 /* Move the rest of the list down to fill the gap */
2604 mch_memmove(rep, rep + 1,
2605 (REPLY_COUNT - i - 1) * sizeof(reply_T));
2606 --REPLY_COUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002607 }
2608
2609 /* Return the reply to the caller, who takes on responsibility
2610 * for freeing it if "remove" is TRUE. */
2611 return reply;
2612 }
2613 }
2614
2615 /* If we got here, we didn't find a reply. Return immediately if the
2616 * "wait" parameter isn't set. */
2617 if (!wait)
2618 break;
2619
2620 /* We need to wait for a reply. Enter a message loop until the
2621 * "reply_received" flag gets set. */
2622
2623 /* Loop until we receive a reply */
2624 while (reply_received == 0)
2625 {
2626 /* Wait for a SendMessage() call to us. This could be the reply
2627 * we are waiting for. Use a timeout of a second, to catch the
2628 * situation that the server died unexpectedly. */
2629 MsgWaitForMultipleObjects(0, NULL, TRUE, 1000, QS_ALLINPUT);
2630
2631 /* If the server has died, give up */
2632 if (!IsWindow(server))
2633 return NULL;
2634
2635 serverProcessPendingMessages();
2636 }
2637 }
2638
2639 return NULL;
2640}
2641
2642/*
2643 * Process any messages in the Windows message queue.
2644 */
2645 void
2646serverProcessPendingMessages(void)
2647{
2648 MSG msg;
2649
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02002650 while (pPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651 {
2652 TranslateMessage(&msg);
Bram Moolenaar8c85fa32011-08-10 17:08:03 +02002653 pDispatchMessage(&msg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654 }
2655}
2656
2657#endif /* FEAT_CLIENTSERVER */
2658
2659#if defined(FEAT_GUI) || (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) \
2660 || defined(PROTO)
2661
2662struct charset_pair
2663{
2664 char *name;
2665 BYTE charset;
2666};
2667
2668static struct charset_pair
2669charset_pairs[] =
2670{
2671 {"ANSI", ANSI_CHARSET},
2672 {"CHINESEBIG5", CHINESEBIG5_CHARSET},
2673 {"DEFAULT", DEFAULT_CHARSET},
2674 {"HANGEUL", HANGEUL_CHARSET},
2675 {"OEM", OEM_CHARSET},
2676 {"SHIFTJIS", SHIFTJIS_CHARSET},
2677 {"SYMBOL", SYMBOL_CHARSET},
2678#ifdef WIN3264
2679 {"ARABIC", ARABIC_CHARSET},
2680 {"BALTIC", BALTIC_CHARSET},
2681 {"EASTEUROPE", EASTEUROPE_CHARSET},
2682 {"GB2312", GB2312_CHARSET},
2683 {"GREEK", GREEK_CHARSET},
2684 {"HEBREW", HEBREW_CHARSET},
2685 {"JOHAB", JOHAB_CHARSET},
2686 {"MAC", MAC_CHARSET},
2687 {"RUSSIAN", RUSSIAN_CHARSET},
2688 {"THAI", THAI_CHARSET},
2689 {"TURKISH", TURKISH_CHARSET},
2690# if (!defined(_MSC_VER) || (_MSC_VER > 1010)) \
2691 && (!defined(__BORLANDC__) || (__BORLANDC__ > 0x0500))
2692 {"VIETNAMESE", VIETNAMESE_CHARSET},
2693# endif
2694#endif
2695 {NULL, 0}
2696};
2697
2698/*
2699 * Convert a charset ID to a name.
2700 * Return NULL when not recognized.
2701 */
2702 char *
2703charset_id2name(int id)
2704{
2705 struct charset_pair *cp;
2706
2707 for (cp = charset_pairs; cp->name != NULL; ++cp)
2708 if ((BYTE)id == cp->charset)
2709 break;
2710 return cp->name;
2711}
2712
2713static const LOGFONT s_lfDefault =
2714{
2715 -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2716 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2717 PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE,
2718 "Fixedsys" /* see _ReadVimIni */
2719};
2720
2721/* Initialise the "current height" to -12 (same as s_lfDefault) just
2722 * in case the user specifies a font in "guifont" with no size before a font
2723 * with an explicit size has been set. This defaults the size to this value
2724 * (-12 equates to roughly 9pt).
2725 */
2726int current_font_height = -12; /* also used in gui_w48.c */
2727
2728/* Convert a string representing a point size into pixels. The string should
2729 * be a positive decimal number, with an optional decimal point (eg, "12", or
2730 * "10.5"). The pixel value is returned, and a pointer to the next unconverted
2731 * character is stored in *end. The flag "vertical" says whether this
2732 * calculation is for a vertical (height) size or a horizontal (width) one.
2733 */
2734 static int
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002735points_to_pixels(char_u *str, char_u **end, int vertical, long_i pprinter_dc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736{
2737 int pixels;
2738 int points = 0;
2739 int divisor = 0;
2740 HWND hwnd = (HWND)0;
2741 HDC hdc;
2742 HDC printer_dc = (HDC)pprinter_dc;
2743
2744 while (*str != NUL)
2745 {
2746 if (*str == '.' && divisor == 0)
2747 {
2748 /* Start keeping a divisor, for later */
2749 divisor = 1;
2750 }
2751 else
2752 {
2753 if (!VIM_ISDIGIT(*str))
2754 break;
2755
2756 points *= 10;
2757 points += *str - '0';
2758 divisor *= 10;
2759 }
2760 ++str;
2761 }
2762
2763 if (divisor == 0)
2764 divisor = 1;
2765
2766 if (printer_dc == NULL)
2767 {
2768 hwnd = GetDesktopWindow();
2769 hdc = GetWindowDC(hwnd);
2770 }
2771 else
2772 hdc = printer_dc;
2773
2774 pixels = MulDiv(points,
2775 GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX),
2776 72 * divisor);
2777
2778 if (printer_dc == NULL)
2779 ReleaseDC(hwnd, hdc);
2780
2781 *end = str;
2782 return pixels;
2783}
2784
Bram Moolenaar9ba0eb82005-06-13 22:28:56 +00002785/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 static int CALLBACK
2787font_enumproc(
2788 ENUMLOGFONT *elf,
2789 NEWTEXTMETRIC *ntm,
2790 int type,
2791 LPARAM lparam)
2792{
2793 /* Return value:
2794 * 0 = terminate now (monospace & ANSI)
2795 * 1 = continue, still no luck...
2796 * 2 = continue, but we have an acceptable LOGFONT
2797 * (monospace, not ANSI)
2798 * We use these values, as EnumFontFamilies returns 1 if the
2799 * callback function is never called. So, we check the return as
2800 * 0 = perfect, 2 = OK, 1 = no good...
2801 * It's not pretty, but it works!
2802 */
2803
2804 LOGFONT *lf = (LOGFONT *)(lparam);
2805
2806#ifndef FEAT_PROPORTIONAL_FONTS
2807 /* Ignore non-monospace fonts without further ado */
2808 if ((ntm->tmPitchAndFamily & 1) != 0)
2809 return 1;
2810#endif
2811
2812 /* Remember this LOGFONT as a "possible" */
2813 *lf = elf->elfLogFont;
2814
2815 /* Terminate the scan as soon as we find an ANSI font */
2816 if (lf->lfCharSet == ANSI_CHARSET
2817 || lf->lfCharSet == OEM_CHARSET
2818 || lf->lfCharSet == DEFAULT_CHARSET)
2819 return 0;
2820
2821 /* Continue the scan - we have a non-ANSI font */
2822 return 2;
2823}
2824
2825 static int
2826init_logfont(LOGFONT *lf)
2827{
2828 int n;
2829 HWND hwnd = GetDesktopWindow();
2830 HDC hdc = GetWindowDC(hwnd);
2831
2832 n = EnumFontFamilies(hdc,
2833 (LPCSTR)lf->lfFaceName,
2834 (FONTENUMPROC)font_enumproc,
2835 (LPARAM)lf);
2836
2837 ReleaseDC(hwnd, hdc);
2838
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02002839 /* If we couldn't find a usable font, return failure */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002840 if (n == 1)
2841 return FAIL;
2842
2843 /* Tidy up the rest of the LOGFONT structure. We set to a basic
2844 * font - get_logfont() sets bold, italic, etc based on the user's
2845 * input.
2846 */
2847 lf->lfHeight = current_font_height;
2848 lf->lfWidth = 0;
2849 lf->lfItalic = FALSE;
2850 lf->lfUnderline = FALSE;
2851 lf->lfStrikeOut = FALSE;
2852 lf->lfWeight = FW_NORMAL;
2853
2854 /* Return success */
2855 return OK;
2856}
2857
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002858/*
2859 * Get font info from "name" into logfont "lf".
2860 * Return OK for a valid name, FAIL otherwise.
2861 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002862 int
2863get_logfont(
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002864 LOGFONT *lf,
2865 char_u *name,
2866 HDC printer_dc,
2867 int verbose)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868{
2869 char_u *p;
2870 int i;
2871 static LOGFONT *lastlf = NULL;
2872
2873 *lf = s_lfDefault;
2874 if (name == NULL)
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002875 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876
2877 if (STRCMP(name, "*") == 0)
2878 {
2879#if defined(FEAT_GUI_W32)
2880 CHOOSEFONT cf;
2881 /* if name is "*", bring up std font dialog: */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02002882 vim_memset(&cf, 0, sizeof(cf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883 cf.lStructSize = sizeof(cf);
2884 cf.hwndOwner = s_hwnd;
2885 cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY | CF_INITTOLOGFONTSTRUCT;
2886 if (lastlf != NULL)
2887 *lf = *lastlf;
2888 cf.lpLogFont = lf;
2889 cf.nFontType = 0 ; //REGULAR_FONTTYPE;
2890 if (ChooseFont(&cf))
2891 goto theend;
2892#else
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002893 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002894#endif
2895 }
2896
2897 /*
2898 * Split name up, it could be <name>:h<height>:w<width> etc.
2899 */
2900 for (p = name; *p && *p != ':'; p++)
2901 {
2902 if (p - name + 1 > LF_FACESIZE)
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002903 return FAIL; /* Name too long */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 lf->lfFaceName[p - name] = *p;
2905 }
2906 if (p != name)
2907 lf->lfFaceName[p - name] = NUL;
2908
2909 /* First set defaults */
2910 lf->lfHeight = -12;
2911 lf->lfWidth = 0;
2912 lf->lfWeight = FW_NORMAL;
2913 lf->lfItalic = FALSE;
2914 lf->lfUnderline = FALSE;
2915 lf->lfStrikeOut = FALSE;
2916
2917 /*
2918 * If the font can't be found, try replacing '_' by ' '.
2919 */
2920 if (init_logfont(lf) == FAIL)
2921 {
2922 int did_replace = FALSE;
2923
2924 for (i = 0; lf->lfFaceName[i]; ++i)
2925 if (lf->lfFaceName[i] == '_')
2926 {
2927 lf->lfFaceName[i] = ' ';
2928 did_replace = TRUE;
2929 }
2930 if (!did_replace || init_logfont(lf) == FAIL)
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002931 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002932 }
2933
2934 while (*p == ':')
2935 p++;
2936
2937 /* Set the values found after ':' */
2938 while (*p)
2939 {
2940 switch (*p++)
2941 {
2942 case 'h':
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002943 lf->lfHeight = - points_to_pixels(p, &p, TRUE, (long_i)printer_dc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944 break;
2945 case 'w':
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002946 lf->lfWidth = points_to_pixels(p, &p, FALSE, (long_i)printer_dc);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002947 break;
2948 case 'b':
2949#ifndef MSWIN16_FASTTEXT
2950 lf->lfWeight = FW_BOLD;
2951#endif
2952 break;
2953 case 'i':
2954#ifndef MSWIN16_FASTTEXT
2955 lf->lfItalic = TRUE;
2956#endif
2957 break;
2958 case 'u':
2959 lf->lfUnderline = TRUE;
2960 break;
2961 case 's':
2962 lf->lfStrikeOut = TRUE;
2963 break;
2964 case 'c':
2965 {
2966 struct charset_pair *cp;
2967
2968 for (cp = charset_pairs; cp->name != NULL; ++cp)
2969 if (STRNCMP(p, cp->name, strlen(cp->name)) == 0)
2970 {
2971 lf->lfCharSet = cp->charset;
2972 p += strlen(cp->name);
2973 break;
2974 }
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002975 if (cp->name == NULL && verbose)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976 {
Bram Moolenaar051b7822005-05-19 21:00:46 +00002977 vim_snprintf((char *)IObuff, IOSIZE,
2978 _("E244: Illegal charset name \"%s\" in font name \"%s\""), p, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979 EMSG(IObuff);
2980 break;
2981 }
2982 break;
2983 }
2984 default:
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002985 if (verbose)
2986 {
Bram Moolenaar051b7822005-05-19 21:00:46 +00002987 vim_snprintf((char *)IObuff, IOSIZE,
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002988 _("E245: Illegal char '%c' in font name \"%s\""),
2989 p[-1], name);
2990 EMSG(IObuff);
2991 }
2992 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002993 }
2994 while (*p == ':')
2995 p++;
2996 }
2997
2998#if defined(FEAT_GUI_W32)
2999theend:
3000#endif
3001 /* ron: init lastlf */
3002 if (printer_dc == NULL)
3003 {
3004 vim_free(lastlf);
3005 lastlf = (LOGFONT *)alloc(sizeof(LOGFONT));
3006 if (lastlf != NULL)
3007 mch_memmove(lastlf, lf, sizeof(LOGFONT));
3008 }
3009
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00003010 return OK;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003011}
3012
3013#endif /* defined(FEAT_GUI) || defined(FEAT_PRINTER) */