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