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