1 /**************************************************************************** 2 * Copyright 2020 Thomas E. Dickey * 3 * Copyright 1998-2009,2010 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30 /**************************************************************************** 31 * Author: Juergen Pfeifer * 32 * and: Thomas E. Dickey * 33 ****************************************************************************/ 34 35 /* 36 * TODO - GetMousePos(POINT * result) from ntconio.c 37 */ 38 39 #include <curses.priv.h> 40 41 MODULE_ID("$Id: lib_win32con.c,v 1.6 2020/11/21 23:44:58 tom Exp $") 42 43 #ifdef _NC_WINDOWS 44 45 #ifdef _NC_MINGW 46 #include <wchar.h> 47 #else 48 #include <tchar.h> 49 #endif 50 51 #include <io.h> 52 53 #if USE_WIDEC_SUPPORT 54 #define write_screen WriteConsoleOutputW 55 #define read_screen ReadConsoleOutputW 56 #else 57 #define write_screen WriteConsoleOutput 58 #define read_screen ReadConsoleOutput 59 #endif 60 61 static BOOL IsConsoleHandle(HANDLE hdl); 62 static bool save_original_screen(void); 63 static bool restore_original_screen(void) GCC_UNUSED; 64 static bool read_screen_data(void); 65 static int Adjust(int milliseconds, int diff); 66 static int decode_mouse(SCREEN *sp, int mask); 67 static bool handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer); 68 static int rkeycompare(const void *el1, const void *el2); 69 static int keycompare(const void *el1, const void *el2); 70 static int MapKey(WORD vKey); 71 static int AnsiKey(WORD vKey); 72 73 static ULONGLONG tdiff(FILETIME fstart, FILETIME fend); 74 75 #define GenMap(vKey,key) MAKELONG(key, vKey) 76 static const LONG keylist[] = 77 { 78 GenMap(VK_PRIOR, KEY_PPAGE), 79 GenMap(VK_NEXT, KEY_NPAGE), 80 GenMap(VK_END, KEY_END), 81 GenMap(VK_HOME, KEY_HOME), 82 GenMap(VK_LEFT, KEY_LEFT), 83 GenMap(VK_UP, KEY_UP), 84 GenMap(VK_RIGHT, KEY_RIGHT), 85 GenMap(VK_DOWN, KEY_DOWN), 86 GenMap(VK_DELETE, KEY_DC), 87 GenMap(VK_INSERT, KEY_IC) 88 }; 89 static const LONG ansi_keys[] = 90 { 91 GenMap(VK_PRIOR, 'I'), 92 GenMap(VK_NEXT, 'Q'), 93 GenMap(VK_END, 'O'), 94 GenMap(VK_HOME, 'H'), 95 GenMap(VK_LEFT, 'K'), 96 GenMap(VK_UP, 'H'), 97 GenMap(VK_RIGHT, 'M'), 98 GenMap(VK_DOWN, 'P'), 99 GenMap(VK_DELETE, 'S'), 100 GenMap(VK_INSERT, 'R') 101 }; 102 #define array_length(a) (sizeof(a)/sizeof(a[0])) 103 #define N_INI ((int)array_length(keylist)) 104 #define FKEYS 24 105 #define MAPSIZE (FKEYS + N_INI) 106 107 /* A process can only have a single console, so it's safe 108 to maintain all the information about it in a single 109 static structure. 110 */ 111 NCURSES_EXPORT_VAR(ConsoleInfo) _nc_CONSOLE; 112 static bool console_initialized = FALSE; 113 114 #define EnsureInit() (void)(console_initialized ? TRUE : _nc_console_checkinit(TRUE, TRUE)) 115 116 #define REQUIRED_MAX_V (DWORD)10 117 #define REQUIRED_MIN_V (DWORD)0 118 #define REQUIRED_BUILD (DWORD)17763 119 /* 120 This function returns 0 if the Windows version has no support for 121 the modern Console interface, otherwise it returns 1 122 */ 123 NCURSES_EXPORT(int) 124 _nc_console_vt_supported(void) 125 { 126 OSVERSIONINFO osvi; 127 int res = 0; 128 129 T((T_CALLED("lib_win32con::_nc_console_vt_supported"))); 130 ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); 131 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 132 133 GetVersionEx(&osvi); 134 T(("GetVersionEx returnedMajor=%ld, Minor=%ld, Build=%ld", 135 osvi.dwMajorVersion, 136 osvi.dwMinorVersion, 137 osvi.dwBuildNumber)); 138 if (osvi.dwMajorVersion >= REQUIRED_MAX_V) { 139 if (osvi.dwMajorVersion == REQUIRED_MAX_V) { 140 if (((osvi.dwMinorVersion == REQUIRED_MIN_V) && 141 (osvi.dwBuildNumber >= REQUIRED_BUILD)) || 142 ((osvi.dwMinorVersion > REQUIRED_MIN_V))) 143 res = 1; 144 } else 145 res = 1; 146 } 147 returnCode(res); 148 } 149 150 NCURSES_EXPORT(void) 151 _nc_console_size(int* Lines, int* Cols) 152 { 153 EnsureInit(); 154 if (Lines != NULL && Cols != NULL) { 155 if (WINCONSOLE.buffered) { 156 *Lines = (int) (WINCONSOLE.SBI.dwSize.Y); 157 *Cols = (int) (WINCONSOLE.SBI.dwSize.X); 158 } else { 159 *Lines = (int) (WINCONSOLE.SBI.srWindow.Bottom + 1 - 160 WINCONSOLE.SBI.srWindow.Top); 161 *Cols = (int) (WINCONSOLE.SBI.srWindow.Right + 1 - 162 WINCONSOLE.SBI.srWindow.Left); 163 } 164 } 165 } 166 167 /* Convert a file descriptor into a HANDLE 168 That's not necessarily a console HANDLE 169 */ 170 NCURSES_EXPORT(HANDLE) 171 _nc_console_handle(int fd) 172 { 173 intptr_t value = _get_osfhandle(fd); 174 return (HANDLE) value; 175 } 176 177 /* Validate that a HANDLE is actually a 178 console HANDLE 179 */ 180 static BOOL 181 IsConsoleHandle(HANDLE hdl) 182 { 183 DWORD dwFlag = 0; 184 BOOL result = FALSE; 185 186 T((T_CALLED("lib_win32con::IsConsoleHandle(HANDLE=%p"), hdl)); 187 188 EnsureInit(); 189 190 if (!GetConsoleMode(hdl, &dwFlag)) { 191 T(("GetConsoleMode failed")); 192 } else { 193 result = TRUE; 194 } 195 196 returnBool(result); 197 } 198 199 /* This is used when running in terminfo mode to discover, 200 whether or not the "terminal" is actually a Windows 201 Console. It's the responsibility of the console to deal 202 with the terminal escape sequences that are sent by 203 terminfo. 204 */ 205 NCURSES_EXPORT(int) 206 _nc_console_test(int fd) 207 { 208 int code = 0; 209 HANDLE hdl = INVALID_HANDLE_VALUE; 210 T((T_CALLED("lib_win32con::_nc_console_test(%d)"), fd)); 211 hdl = _nc_console_handle(fd); 212 code = (int) IsConsoleHandle(hdl); 213 returnCode(code); 214 } 215 216 #define OutHandle() ((WINCONSOLE.isTermInfoConsole || WINCONSOLE.progMode) ? WINCONSOLE.hdl : WINCONSOLE.out) 217 218 NCURSES_EXPORT(void) 219 _nc_console_selectActiveHandle(void) 220 { 221 if (WINCONSOLE.lastOut != WINCONSOLE.hdl) { 222 WINCONSOLE.lastOut = WINCONSOLE.hdl; 223 SetConsoleActiveScreenBuffer(WINCONSOLE.lastOut); 224 } 225 } 226 227 NCURSES_EXPORT(HANDLE) 228 _nc_console_fd2handle(int fd) 229 { 230 HANDLE hdl = _nc_console_handle(fd); 231 if (hdl==WINCONSOLE.inp) { 232 T(("lib_win32con:validateHandle %d -> WINCONSOLE.inp", fd)); 233 } else if (hdl==WINCONSOLE.hdl) { 234 T(("lib_win32con:validateHandle %d -> WINCONSOLE.hdl", fd)); 235 } else if (hdl==WINCONSOLE.out) { 236 T(("lib_win32con:validateHandle %d -> WINCONSOLE.out", fd)); 237 } else { 238 T(("lib_win32con:validateHandle %d maps to unknown HANDLE", fd)); 239 hdl = INVALID_HANDLE_VALUE; 240 } 241 #if 1 242 assert(hdl != INVALID_HANDLE_VALUE); 243 #endif 244 if (hdl != INVALID_HANDLE_VALUE) { 245 if (hdl != WINCONSOLE.inp && (!WINCONSOLE.isTermInfoConsole && WINCONSOLE.progMode)) { 246 if (hdl==WINCONSOLE.out && hdl!=WINCONSOLE.hdl) { 247 T(("lib_win32con:validateHandle forcing WINCONSOLE.out -> WINCONSOLE.hdl")); 248 hdl = WINCONSOLE.hdl; 249 } 250 } 251 } 252 return hdl; 253 } 254 255 NCURSES_EXPORT(int) 256 _nc_console_setmode(HANDLE hdl, const TTY *arg) 257 { 258 DWORD dwFlag = 0; 259 int code = ERR; 260 HANDLE alt; 261 262 if (arg) { 263 #ifdef TRACE 264 TTY TRCTTY; 265 #define TRCTTYOUT(flag) TRCTTY.dwFlagOut = flag 266 #define TRCTTYIN(flag) TRCTTY.dwFlagIn = flag 267 #else 268 #define TRCTTYOUT(flag) 269 #define TRCTTYIN(flag) 270 #endif 271 T(("lib_win32con:_nc_console_setmode %s", _nc_trace_ttymode(arg))); 272 if (hdl==WINCONSOLE.inp) { 273 dwFlag = arg->dwFlagIn | ENABLE_MOUSE_INPUT | VT_FLAG_IN; 274 if (WINCONSOLE.isTermInfoConsole) 275 dwFlag |= (VT_FLAG_IN); 276 else 277 dwFlag &= (DWORD) ~(VT_FLAG_IN); 278 TRCTTYIN(dwFlag); 279 SetConsoleMode(hdl, dwFlag); 280 281 alt = OutHandle(); 282 dwFlag = arg->dwFlagOut; 283 if (WINCONSOLE.isTermInfoConsole) 284 dwFlag |= (VT_FLAG_OUT); 285 else 286 dwFlag |= (VT_FLAG_OUT); 287 TRCTTYOUT(dwFlag); 288 SetConsoleMode(alt, dwFlag); 289 } else { 290 dwFlag = arg->dwFlagOut; 291 if (WINCONSOLE.isTermInfoConsole) 292 dwFlag |= (VT_FLAG_OUT); 293 else 294 dwFlag |= (VT_FLAG_OUT); 295 TRCTTYOUT(dwFlag); 296 SetConsoleMode(hdl, dwFlag); 297 298 alt = WINCONSOLE.inp; 299 dwFlag = arg->dwFlagIn | ENABLE_MOUSE_INPUT; 300 if (WINCONSOLE.isTermInfoConsole) 301 dwFlag |= (VT_FLAG_IN); 302 else 303 dwFlag &= (DWORD) ~(VT_FLAG_IN); 304 TRCTTYIN(dwFlag); 305 SetConsoleMode(alt, dwFlag); 306 T(("effective mode set %s", _nc_trace_ttymode(&TRCTTY))); 307 } 308 code = OK; 309 } 310 return(code); 311 } 312 313 NCURSES_EXPORT(int) 314 _nc_console_getmode(HANDLE hdl, TTY *arg) 315 { 316 int code = ERR; 317 318 if (arg) { 319 DWORD dwFlag = 0; 320 HANDLE alt; 321 322 if (hdl==WINCONSOLE.inp) { 323 if(GetConsoleMode(hdl, &dwFlag)) { 324 arg->dwFlagIn = dwFlag; 325 alt = OutHandle(); 326 if (GetConsoleMode(alt, &dwFlag)) { 327 arg->dwFlagOut = dwFlag; 328 code = OK; 329 } 330 } 331 } else { 332 if (GetConsoleMode(hdl, &dwFlag)) { 333 arg->dwFlagOut = dwFlag; 334 alt = WINCONSOLE.inp; 335 if (GetConsoleMode(alt, &dwFlag)) { 336 arg->dwFlagIn = dwFlag; 337 code = OK; 338 } 339 } 340 } 341 } 342 T(("lib_win32con:_nc_console_getmode %s", _nc_trace_ttymode(arg))); 343 return(code); 344 } 345 346 NCURSES_EXPORT(int) 347 _nc_console_flush(HANDLE hdl) 348 { 349 int code = OK; 350 351 T((T_CALLED("lib_win32con::_nc_console_flush(hdl=%p"), hdl)); 352 353 if (hdl != INVALID_HANDLE_VALUE) { 354 if (hdl == WINCONSOLE.hdl || 355 hdl == WINCONSOLE.inp || 356 hdl == WINCONSOLE.out) { 357 if (!FlushConsoleInputBuffer(WINCONSOLE.inp)) 358 code = ERR; 359 } else { 360 code = ERR; 361 T(("_nc_console_flush not requesting a handle owned by console.")); 362 } 363 } 364 returnCode(code); 365 } 366 367 NCURSES_EXPORT(WORD) 368 _nc_console_MapColor(bool fore, int color) 369 { 370 static const int _cmap[] = 371 {0, 4, 2, 6, 1, 5, 3, 7}; 372 int a; 373 if (color < 0 || color > 7) 374 a = fore ? 7 : 0; 375 else 376 a = _cmap[color]; 377 if (!fore) 378 a = a << 4; 379 return (WORD) a; 380 } 381 382 383 /* 384 * Attempt to save the screen contents. PDCurses does this if 385 * PDC_RESTORE_SCREEN is set, giving the same visual appearance on 386 * restoration as if the library had allocated a console buffer. MSDN 387 * says that the data which can be read is limited to 64Kb (and may be 388 * less). 389 */ 390 static bool 391 save_original_screen(void) 392 { 393 bool result = FALSE; 394 395 WINCONSOLE.save_region.Top = 0; 396 WINCONSOLE.save_region.Left = 0; 397 WINCONSOLE.save_region.Bottom = (SHORT) (WINCONSOLE.SBI.dwSize.Y - 1); 398 WINCONSOLE.save_region.Right = (SHORT) (WINCONSOLE.SBI.dwSize.X - 1); 399 400 if (read_screen_data()) { 401 result = TRUE; 402 } else { 403 404 WINCONSOLE.save_region.Top = WINCONSOLE.SBI.srWindow.Top; 405 WINCONSOLE.save_region.Left = WINCONSOLE.SBI.srWindow.Left; 406 WINCONSOLE.save_region.Bottom = WINCONSOLE.SBI.srWindow.Bottom; 407 WINCONSOLE.save_region.Right = WINCONSOLE.SBI.srWindow.Right; 408 409 WINCONSOLE.window_only = TRUE; 410 411 if (read_screen_data()) { 412 result = TRUE; 413 } 414 } 415 416 T(("... save original screen contents %s", result ? "ok" : "err")); 417 return result; 418 } 419 420 static bool 421 restore_original_screen(void) 422 { 423 COORD bufferCoord; 424 bool result = FALSE; 425 SMALL_RECT save_region = WINCONSOLE.save_region; 426 427 T(("... restoring %s", 428 WINCONSOLE.window_only ? "window" : "entire buffer")); 429 430 bufferCoord.X = (SHORT) (WINCONSOLE.window_only ? 431 WINCONSOLE.SBI.srWindow.Left : 0); 432 bufferCoord.Y = (SHORT) (WINCONSOLE.window_only ? 433 WINCONSOLE.SBI.srWindow.Top : 0); 434 435 if (write_screen(WINCONSOLE.hdl, 436 WINCONSOLE.save_screen, 437 WINCONSOLE.save_size, 438 bufferCoord, 439 &save_region)) { 440 result = TRUE; 441 mvcur(-1, -1, LINES - 2, 0); 442 T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)", 443 WINCONSOLE.save_size.Y, 444 WINCONSOLE.save_size.X, 445 save_region.Top, 446 save_region.Left, 447 save_region.Bottom, 448 save_region.Right)); 449 } else { 450 T(("... restore original screen contents err")); 451 } 452 return result; 453 } 454 455 static bool 456 read_screen_data(void) 457 { 458 bool result = FALSE; 459 COORD bufferCoord; 460 size_t want; 461 462 WINCONSOLE.save_size.X = (SHORT) (WINCONSOLE.save_region.Right 463 - WINCONSOLE.save_region.Left + 1); 464 WINCONSOLE.save_size.Y = (SHORT) (WINCONSOLE.save_region.Bottom 465 - WINCONSOLE.save_region.Top + 1); 466 467 want = (size_t) (WINCONSOLE.save_size.X * WINCONSOLE.save_size.Y); 468 469 if ((WINCONSOLE.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) { 470 bufferCoord.X = (SHORT) (WINCONSOLE.window_only ? 471 WINCONSOLE.SBI.srWindow.Left : 0); 472 bufferCoord.Y = (SHORT) (WINCONSOLE.window_only ? 473 WINCONSOLE.SBI.srWindow.Top : 0); 474 475 T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d", 476 WINCONSOLE.window_only ? "window" : "buffer", 477 WINCONSOLE.save_size.Y, WINCONSOLE.save_size.X, 478 WINCONSOLE.save_region.Top, 479 WINCONSOLE.save_region.Left, 480 WINCONSOLE.save_region.Bottom, 481 WINCONSOLE.save_region.Right, 482 bufferCoord.Y, 483 bufferCoord.X)); 484 485 if (read_screen(WINCONSOLE.hdl, 486 WINCONSOLE.save_screen, 487 WINCONSOLE.save_size, 488 bufferCoord, 489 &WINCONSOLE.save_region)) { 490 result = TRUE; 491 } else { 492 T((" error %#lx", (unsigned long) GetLastError())); 493 FreeAndNull(WINCONSOLE.save_screen); 494 } 495 } 496 497 return result; 498 } 499 500 NCURSES_EXPORT(bool) 501 _nc_console_get_SBI(void) 502 { 503 bool rc = FALSE; 504 if (GetConsoleScreenBufferInfo(WINCONSOLE.hdl, &(WINCONSOLE.SBI))) { 505 T(("GetConsoleScreenBufferInfo")); 506 T(("... buffer(X:%d Y:%d)", 507 WINCONSOLE.SBI.dwSize.X, 508 WINCONSOLE.SBI.dwSize.Y)); 509 T(("... window(X:%d Y:%d)", 510 WINCONSOLE.SBI.dwMaximumWindowSize.X, 511 WINCONSOLE.SBI.dwMaximumWindowSize.Y)); 512 T(("... cursor(X:%d Y:%d)", 513 WINCONSOLE.SBI.dwCursorPosition.X, 514 WINCONSOLE.SBI.dwCursorPosition.Y)); 515 T(("... display(Top:%d Bottom:%d Left:%d Right:%d)", 516 WINCONSOLE.SBI.srWindow.Top, 517 WINCONSOLE.SBI.srWindow.Bottom, 518 WINCONSOLE.SBI.srWindow.Left, 519 WINCONSOLE.SBI.srWindow.Right)); 520 if (WINCONSOLE.buffered) { 521 WINCONSOLE.origin.X = 0; 522 WINCONSOLE.origin.Y = 0; 523 } else { 524 WINCONSOLE.origin.X = WINCONSOLE.SBI.srWindow.Left; 525 WINCONSOLE.origin.Y = WINCONSOLE.SBI.srWindow.Top; 526 } 527 rc = TRUE; 528 } else { 529 T(("GetConsoleScreenBufferInfo ERR")); 530 } 531 return rc; 532 } 533 534 #define MIN_WIDE 80 535 #define MIN_HIGH 24 536 537 /* 538 * In "normal" mode, reset the buffer- and window-sizes back to their original values. 539 */ 540 NCURSES_EXPORT(void) 541 _nc_console_set_scrollback(bool normal, CONSOLE_SCREEN_BUFFER_INFO * info) 542 { 543 SMALL_RECT rect; 544 COORD coord; 545 bool changed = FALSE; 546 547 T((T_CALLED("lib_win32con::_nc_console_set_scrollback(%s)"), 548 (normal 549 ? "normal" 550 : "application"))); 551 552 T(("... SBI.srWindow %d,%d .. %d,%d", 553 info->srWindow.Top, 554 info->srWindow.Left, 555 info->srWindow.Bottom, 556 info->srWindow.Right)); 557 T(("... SBI.dwSize %dx%d", 558 info->dwSize.Y, 559 info->dwSize.X)); 560 561 if (normal) { 562 rect = info->srWindow; 563 coord = info->dwSize; 564 if (memcmp(info, &WINCONSOLE.SBI, sizeof(*info)) != 0) { 565 changed = TRUE; 566 WINCONSOLE.SBI = *info; 567 } 568 } else { 569 int high = info->srWindow.Bottom - info->srWindow.Top + 1; 570 int wide = info->srWindow.Right - info->srWindow.Left + 1; 571 572 if (high < MIN_HIGH) { 573 T(("... height %d < %d", high, MIN_HIGH)); 574 high = MIN_HIGH; 575 changed = TRUE; 576 } 577 if (wide < MIN_WIDE) { 578 T(("... width %d < %d", wide, MIN_WIDE)); 579 wide = MIN_WIDE; 580 changed = TRUE; 581 } 582 583 rect.Left = 584 rect.Top = 0; 585 rect.Right = (SHORT) (wide - 1); 586 rect.Bottom = (SHORT) (high - 1); 587 588 coord.X = (SHORT) wide; 589 coord.Y = (SHORT) high; 590 591 if (info->dwSize.Y != high || 592 info->dwSize.X != wide || 593 info->srWindow.Top != 0 || 594 info->srWindow.Left != 0) { 595 changed = TRUE; 596 } 597 598 } 599 600 if (changed) { 601 T(("... coord %d,%d", coord.Y, coord.X)); 602 T(("... rect %d,%d - %d,%d", 603 rect.Top, rect.Left, 604 rect.Bottom, rect.Right)); 605 SetConsoleScreenBufferSize(WINCONSOLE.hdl, coord); /* dwSize */ 606 SetConsoleWindowInfo(WINCONSOLE.hdl, TRUE, &rect); /* srWindow */ 607 _nc_console_get_SBI(); 608 } 609 returnVoid; 610 } 611 612 static ULONGLONG 613 tdiff(FILETIME fstart, FILETIME fend) 614 { 615 ULARGE_INTEGER ustart; 616 ULARGE_INTEGER uend; 617 ULONGLONG diff; 618 619 ustart.LowPart = fstart.dwLowDateTime; 620 ustart.HighPart = fstart.dwHighDateTime; 621 uend.LowPart = fend.dwLowDateTime; 622 uend.HighPart = fend.dwHighDateTime; 623 624 diff = (uend.QuadPart - ustart.QuadPart) / 10000; 625 return diff; 626 } 627 628 static int 629 Adjust(int milliseconds, int diff) 630 { 631 if (milliseconds != INFINITY) { 632 milliseconds -= diff; 633 if (milliseconds < 0) 634 milliseconds = 0; 635 } 636 return milliseconds; 637 } 638 639 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \ 640 FROM_LEFT_2ND_BUTTON_PRESSED | \ 641 FROM_LEFT_3RD_BUTTON_PRESSED | \ 642 FROM_LEFT_4TH_BUTTON_PRESSED | \ 643 RIGHTMOST_BUTTON_PRESSED) 644 645 static int 646 decode_mouse(SCREEN *sp, int mask) 647 { 648 int result = 0; 649 650 (void) sp; 651 assert(sp && console_initialized); 652 653 if (mask & FROM_LEFT_1ST_BUTTON_PRESSED) 654 result |= BUTTON1_PRESSED; 655 if (mask & FROM_LEFT_2ND_BUTTON_PRESSED) 656 result |= BUTTON2_PRESSED; 657 if (mask & FROM_LEFT_3RD_BUTTON_PRESSED) 658 result |= BUTTON3_PRESSED; 659 if (mask & FROM_LEFT_4TH_BUTTON_PRESSED) 660 result |= BUTTON4_PRESSED; 661 662 if (mask & RIGHTMOST_BUTTON_PRESSED) { 663 switch (WINCONSOLE.numButtons) { 664 case 1: 665 result |= BUTTON1_PRESSED; 666 break; 667 case 2: 668 result |= BUTTON2_PRESSED; 669 break; 670 case 3: 671 result |= BUTTON3_PRESSED; 672 break; 673 case 4: 674 result |= BUTTON4_PRESSED; 675 break; 676 } 677 } 678 679 return result; 680 } 681 682 #define AdjustY() (WINCONSOLE.buffered ? 0 : (int) WINCONSOLE.SBI.srWindow.Top) 683 684 static bool 685 handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer) 686 { 687 MEVENT work; 688 bool result = FALSE; 689 690 assert(sp); 691 692 sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons; 693 sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK; 694 695 /* 696 * We're only interested if the button is pressed or released. 697 * FIXME: implement continuous event-tracking. 698 */ 699 if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) { 700 memset(&work, 0, sizeof(work)); 701 702 if (sp->_drv_mouse_new_buttons) { 703 work.bstate |= 704 (mmask_t) decode_mouse(sp, 705 sp->_drv_mouse_new_buttons); 706 } else { 707 /* cf: BUTTON_PRESSED, BUTTON_RELEASED */ 708 work.bstate |= 709 (mmask_t) (decode_mouse(sp, 710 sp->_drv_mouse_old_buttons) 711 >> 1); 712 result = TRUE; 713 } 714 715 work.x = mer.dwMousePosition.X; 716 work.y = mer.dwMousePosition.Y - AdjustY(); 717 718 sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work; 719 sp->_drv_mouse_tail += 1; 720 } 721 return result; 722 } 723 724 static int 725 rkeycompare(const void *el1, const void *el2) 726 { 727 WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff; 728 WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff; 729 730 return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1)); 731 } 732 733 734 static int 735 keycompare(const void *el1, const void *el2) 736 { 737 WORD key1 = HIWORD((*((const LONG *) el1))); 738 WORD key2 = HIWORD((*((const LONG *) el2))); 739 740 return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1)); 741 } 742 743 static int 744 MapKey(WORD vKey) 745 { 746 int code = -1; 747 748 if (!WINCONSOLE.isTermInfoConsole) { 749 WORD nKey = 0; 750 void *res; 751 LONG key = GenMap(vKey, 0); 752 753 res = bsearch(&key, 754 WINCONSOLE.map, 755 (size_t) (N_INI + FKEYS), 756 sizeof(keylist[0]), 757 keycompare); 758 if (res) { 759 key = *((LONG *) res); 760 nKey = LOWORD(key); 761 code = (int) (nKey & 0x7fff); 762 if (nKey & 0x8000) 763 code = -code; 764 } 765 } 766 return code; 767 } 768 769 static int 770 AnsiKey(WORD vKey) 771 { 772 int code = -1; 773 774 if (!WINCONSOLE.isTermInfoConsole) { 775 WORD nKey = 0; 776 void *res; 777 LONG key = GenMap(vKey, 0); 778 779 res = bsearch(&key, 780 WINCONSOLE.ansi_map, 781 (size_t) (N_INI + FKEYS), 782 sizeof(keylist[0]), 783 keycompare); 784 if (res) { 785 key = *((LONG *) res); 786 nKey = LOWORD(key); 787 code = (int) (nKey & 0x7fff); 788 if (nKey & 0x8000) 789 code = -code; 790 } 791 } 792 return code; 793 } 794 795 NCURSES_EXPORT(int) 796 _nc_console_keyok(int keycode, int flag) 797 { 798 int code = ERR; 799 WORD nKey; 800 WORD vKey; 801 void *res; 802 LONG key = GenMap(0, (WORD) keycode); 803 804 T((T_CALLED("lib_win32con::_nc_console_keyok(%d, %d)"), keycode, flag)); 805 806 res = bsearch(&key, 807 WINCONSOLE.rmap, 808 (size_t) (N_INI + FKEYS), 809 sizeof(keylist[0]), 810 rkeycompare); 811 if (res) { 812 key = *((LONG *) res); 813 vKey = HIWORD(key); 814 nKey = (LOWORD(key)) & 0x7fff; 815 if (!flag) 816 nKey |= 0x8000; 817 *(LONG *) res = GenMap(vKey, nKey); 818 } 819 returnCode(code); 820 } 821 822 NCURSES_EXPORT(bool) 823 _nc_console_keyExist(int keycode) 824 { 825 WORD nKey; 826 void *res; 827 bool found = FALSE; 828 LONG key = GenMap(0, (WORD) keycode); 829 830 T((T_CALLED("lib_win32con::_nc_console_keyExist(%d)"), keycode)); 831 res = bsearch(&key, 832 WINCONSOLE.rmap, 833 (size_t) (N_INI + FKEYS), 834 sizeof(keylist[0]), 835 rkeycompare); 836 if (res) { 837 key = *((LONG *) res); 838 nKey = LOWORD(key); 839 if (!(nKey & 0x8000)) 840 found = TRUE; 841 } 842 returnCode(found); 843 } 844 845 NCURSES_EXPORT(int) 846 _nc_console_twait( 847 SCREEN *sp, 848 HANDLE hdl, 849 int mode, 850 int milliseconds, 851 int *timeleft 852 EVENTLIST_2nd(_nc_eventlist * evl)) 853 { 854 INPUT_RECORD inp_rec; 855 BOOL b; 856 DWORD nRead = 0, rc = (DWORD) (-1); 857 int code = 0; 858 FILETIME fstart; 859 FILETIME fend; 860 int diff; 861 bool isNoDelay = (milliseconds == 0); 862 863 #ifdef NCURSES_WGETCH_EVENTS 864 (void) evl; /* TODO: implement wgetch-events */ 865 #endif 866 867 #define IGNORE_CTRL_KEYS (SHIFT_PRESSED|LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED| \ 868 LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED) 869 #define CONSUME() ReadConsoleInput(hdl, &inp_rec, 1, &nRead) 870 871 assert(sp); 872 873 TR(TRACE_IEVENT, ("start twait: hdl=%p, %d milliseconds, mode: %d", 874 hdl, milliseconds, mode)); 875 876 if (milliseconds < 0) 877 milliseconds = INFINITY; 878 879 memset(&inp_rec, 0, sizeof(inp_rec)); 880 881 while (true) { 882 if (!isNoDelay) { 883 GetSystemTimeAsFileTime(&fstart); 884 rc = WaitForSingleObject(hdl, (DWORD) milliseconds); 885 GetSystemTimeAsFileTime(&fend); 886 diff = (int) tdiff(fstart, fend); 887 milliseconds = Adjust(milliseconds, diff); 888 if (milliseconds< 0) 889 break; 890 } 891 892 if (isNoDelay || (rc == WAIT_OBJECT_0)) { 893 if (mode) { 894 nRead = 0; 895 b = GetNumberOfConsoleInputEvents(hdl, &nRead); 896 if (!b) { 897 T(("twait:err GetNumberOfConsoleInputEvents")); 898 } 899 if (isNoDelay && b) { 900 T(("twait: Events Available: %ld", nRead)); 901 if (nRead==0) { 902 code = 0; 903 goto end; 904 } else { 905 DWORD n = 0; 906 INPUT_RECORD* pInpRec = 907 TypeAlloca(INPUT_RECORD, nRead); 908 if (pInpRec != NULL) { 909 DWORD i; 910 BOOL f; 911 memset(pInpRec, 0, sizeof(INPUT_RECORD)*nRead); 912 f = PeekConsoleInput(hdl, pInpRec, nRead, &n); 913 if (f) { 914 for(i = 0; i < n; i++) { 915 if (pInpRec[i].EventType==KEY_EVENT) { 916 if(pInpRec[i].Event.KeyEvent.bKeyDown) { 917 DWORD ctrlMask = 918 (pInpRec[i].Event.KeyEvent.dwControlKeyState & 919 IGNORE_CTRL_KEYS); 920 if (!ctrlMask) { 921 code = TW_INPUT; 922 goto end; 923 } 924 } 925 } 926 } 927 } else { 928 T(("twait:err PeekConsoleInput")); 929 } 930 code = 0; 931 goto end; 932 } else { 933 T(("twait:err could not alloca input records")); 934 } 935 } 936 } 937 if (b && nRead > 0) { 938 b = PeekConsoleInput(hdl, &inp_rec, 1, &nRead); 939 if (!b) { 940 T(("twait:err PeekConsoleInput")); 941 } 942 if (b && nRead > 0) { 943 switch (inp_rec.EventType) { 944 case KEY_EVENT: 945 if (mode & TW_INPUT) { 946 WORD vk = 947 inp_rec.Event.KeyEvent.wVirtualKeyCode; 948 char ch = 949 inp_rec.Event.KeyEvent.uChar.AsciiChar; 950 T(("twait:event KEY_EVENT")); 951 T(("twait vk=%d, ch=%d, keydown=%d", 952 vk, ch, inp_rec.Event.KeyEvent.bKeyDown)); 953 if (inp_rec.Event.KeyEvent.bKeyDown) { 954 T(("twait:event KeyDown")); 955 if (!WINCONSOLE.isTermInfoConsole && 956 (0 == ch)) { 957 int nKey = MapKey(vk); 958 if (nKey < 0) { 959 CONSUME(); 960 continue; 961 } 962 } 963 code = TW_INPUT; 964 goto end; 965 } else { 966 CONSUME(); 967 } 968 } 969 continue; 970 case MOUSE_EVENT: 971 T(("twait:event MOUSE_EVENT")); 972 if (decode_mouse(sp, 973 (inp_rec.Event.MouseEvent.dwButtonState 974 & BUTTON_MASK)) == 0) { 975 CONSUME(); 976 } else if (mode & TW_MOUSE) { 977 code = TW_MOUSE; 978 goto end; 979 } 980 continue; 981 /* e.g., FOCUS_EVENT */ 982 default: 983 T(("twait:event Tyoe %d", inp_rec.EventType)); 984 CONSUME(); 985 _nc_console_selectActiveHandle(); 986 continue; 987 } 988 } 989 } 990 } 991 continue; 992 } else { 993 if (rc != WAIT_TIMEOUT) { 994 code = -1; 995 break; 996 } else { 997 code = 0; 998 break; 999 } 1000 } 1001 } 1002 end: 1003 1004 TR(TRACE_IEVENT, ("end twait: returned %d (%lu), remaining time %d msec", 1005 code, GetLastError(), milliseconds)); 1006 1007 if (timeleft) 1008 *timeleft = milliseconds; 1009 1010 return code; 1011 } 1012 1013 NCURSES_EXPORT(int) 1014 _nc_console_testmouse( 1015 SCREEN *sp, 1016 HANDLE hdl, 1017 int delay 1018 EVENTLIST_2nd(_nc_eventlist * evl)) 1019 { 1020 int rc = 0; 1021 1022 assert(sp); 1023 1024 if (sp->_drv_mouse_head < sp->_drv_mouse_tail) { 1025 rc = TW_MOUSE; 1026 } else { 1027 rc = _nc_console_twait(sp, 1028 hdl, 1029 TWAIT_MASK, 1030 delay, 1031 (int *) 0 1032 EVENTLIST_2nd(evl)); 1033 } 1034 return rc; 1035 } 1036 1037 NCURSES_EXPORT(int) 1038 _nc_console_read( 1039 SCREEN *sp, 1040 HANDLE hdl, 1041 int *buf) 1042 { 1043 int rc = -1; 1044 INPUT_RECORD inp_rec; 1045 BOOL b; 1046 DWORD nRead; 1047 WORD vk; 1048 1049 assert(sp); 1050 assert(buf); 1051 1052 memset(&inp_rec, 0, sizeof(inp_rec)); 1053 1054 T((T_CALLED("lib_win32con::_nc_console_read(%p)"), sp)); 1055 1056 while ((b = ReadConsoleInput(hdl, &inp_rec, 1, &nRead))) { 1057 if (b && nRead > 0) { 1058 if (rc < 0) 1059 rc = 0; 1060 rc = rc + (int) nRead; 1061 if (inp_rec.EventType == KEY_EVENT) { 1062 if (!inp_rec.Event.KeyEvent.bKeyDown) 1063 continue; 1064 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar; 1065 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode; 1066 /* 1067 * There are 24 virtual function-keys, and typically 1068 * 12 function-keys on a keyboard. Use the shift-modifier 1069 * to provide the remaining 12 keys. 1070 */ 1071 if (vk >= VK_F1 && vk <= VK_F12) { 1072 if (inp_rec.Event.KeyEvent.dwControlKeyState & 1073 SHIFT_PRESSED) { 1074 vk = (WORD) (vk + 12); 1075 } 1076 } 1077 if (*buf == 0) { 1078 int key = MapKey(vk); 1079 if (key < 0) 1080 continue; 1081 if (sp->_keypad_on) { 1082 *buf = key; 1083 } else { 1084 ungetch('\0'); 1085 *buf = AnsiKey(vk); 1086 } 1087 } 1088 break; 1089 } else if (inp_rec.EventType == MOUSE_EVENT) { 1090 if (handle_mouse(sp, 1091 inp_rec.Event.MouseEvent)) { 1092 *buf = KEY_MOUSE; 1093 break; 1094 } 1095 } 1096 continue; 1097 } 1098 } 1099 returnCode(rc); 1100 } 1101 1102 /* Our replacement for the systems _isatty to include also 1103 a test for mintty. This is called from the NC_ISATTY macro 1104 defined in curses.priv.h 1105 1106 Return codes: 1107 - 0 : Not a TTY 1108 - 1 : A Windows character device detected by _isatty 1109 - 2 : A future implementation may return 2 for mintty 1110 */ 1111 NCURSES_EXPORT(int) 1112 _nc_console_isatty(int fd) 1113 { 1114 int result = 0; 1115 T((T_CALLED("lib_win32con::_nc_console_isatty(%d"), fd)); 1116 1117 if (_isatty(fd)) 1118 result = 1; 1119 #ifdef _NC_CHECK_MINTTY 1120 else { 1121 if (_nc_console_checkmintty(fd, NULL)) { 1122 result = 2; 1123 fprintf(stderr, "ncurses on Windows must run in a Windows console.\n"); 1124 fprintf(stderr, "On newer versions of Windows, the calling program should create a PTY-like.\n"); 1125 fprintf(stderr, "device using the CreatePseudoConsole Windows API call.\n"); 1126 exit(EXIT_FAILURE); 1127 } 1128 } 1129 #endif 1130 returnCode(result); 1131 } 1132 1133 NCURSES_EXPORT(bool) 1134 _nc_console_checkinit(bool initFlag, bool assumeTermInfo) 1135 { 1136 bool res = FALSE; 1137 1138 T((T_CALLED("lib_win32con::_nc_console_checkinit(initFlag=%d, assumeTermInfo=%d)"), 1139 initFlag, assumeTermInfo)); 1140 1141 if (!initFlag) { 1142 res = console_initialized; 1143 } else { 1144 /* initialize once, or not at all */ 1145 if (!console_initialized) { 1146 int i; 1147 DWORD num_buttons; 1148 WORD a; 1149 BOOL buffered = FALSE; 1150 BOOL b; 1151 1152 START_TRACE(); 1153 WINCONSOLE.isTermInfoConsole = assumeTermInfo; 1154 1155 WINCONSOLE.map = (LPDWORD)malloc(sizeof(DWORD)*MAPSIZE); 1156 WINCONSOLE.rmap = (LPDWORD)malloc(sizeof(DWORD)*MAPSIZE); 1157 WINCONSOLE.ansi_map = (LPDWORD)malloc(sizeof(DWORD)*MAPSIZE); 1158 1159 for (i = 0; i < (N_INI + FKEYS); i++) { 1160 if (i < N_INI) { 1161 WINCONSOLE.rmap[i] = WINCONSOLE.map[i] = 1162 (DWORD) keylist[i]; 1163 WINCONSOLE.ansi_map[i] = (DWORD) ansi_keys[i]; 1164 } else { 1165 WINCONSOLE.rmap[i] = WINCONSOLE.map[i] = 1166 (DWORD) GenMap((VK_F1 + (i - N_INI)), 1167 (KEY_F(1) + (i - N_INI))); 1168 WINCONSOLE.ansi_map[i] = 1169 (DWORD) GenMap((VK_F1 + (i - N_INI)), 1170 (';' + (i - N_INI))); 1171 } 1172 } 1173 qsort(WINCONSOLE.ansi_map, 1174 (size_t) (MAPSIZE), 1175 sizeof(keylist[0]), 1176 keycompare); 1177 qsort(WINCONSOLE.map, 1178 (size_t) (MAPSIZE), 1179 sizeof(keylist[0]), 1180 keycompare); 1181 qsort(WINCONSOLE.rmap, 1182 (size_t) (MAPSIZE), 1183 sizeof(keylist[0]), 1184 rkeycompare); 1185 1186 if (GetNumberOfConsoleMouseButtons(&num_buttons)) { 1187 WINCONSOLE.numButtons = (int) num_buttons; 1188 } else { 1189 WINCONSOLE.numButtons = 1; 1190 } 1191 1192 a = _nc_console_MapColor(true, COLOR_WHITE) | 1193 _nc_console_MapColor(false, COLOR_BLACK); 1194 for (i = 0; i < CON_NUMPAIRS; i++) 1195 WINCONSOLE.pairs[i] = a; 1196 1197 WINCONSOLE.inp = GetStdHandle(STD_INPUT_HANDLE); 1198 WINCONSOLE.out = GetStdHandle(STD_OUTPUT_HANDLE); 1199 WINCONSOLE.hdl = WINCONSOLE.out; 1200 1201 GetConsoleMode(WINCONSOLE.inp, &WINCONSOLE.originalMode.dwFlagIn); 1202 GetConsoleMode(WINCONSOLE.out, &WINCONSOLE.originalMode.dwFlagOut); 1203 1204 if (!WINCONSOLE.isTermInfoConsole) { 1205 b = AllocConsole(); 1206 1207 if (!b) 1208 b = AttachConsole(ATTACH_PARENT_PROCESS); 1209 1210 if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) { 1211 T(("... will not buffer console")); 1212 } else { 1213 T(("... creating console buffer")); 1214 WINCONSOLE.hdl = 1215 CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 1216 0, 1217 NULL, 1218 CONSOLE_TEXTMODE_BUFFER, 1219 NULL); 1220 buffered = TRUE; 1221 } 1222 } 1223 1224 /* We set binary I/O even when using the console 1225 driver to cover the situation, that the 1226 TERM variable is set to #win32con, but actually 1227 Windows supports virtual terminal processing. 1228 So if terminfo functions are used in this setup, 1229 they actually may work. 1230 */ 1231 _setmode(fileno(stdin), _O_BINARY); 1232 _setmode(fileno(stdout), _O_BINARY); 1233 1234 if (WINCONSOLE.hdl != INVALID_HANDLE_VALUE) { 1235 WINCONSOLE.buffered = buffered; 1236 _nc_console_get_SBI(); 1237 WINCONSOLE.save_SBI = WINCONSOLE.SBI; 1238 if (!buffered) { 1239 save_original_screen(); 1240 _nc_console_set_scrollback(FALSE, &WINCONSOLE.SBI); 1241 } 1242 GetConsoleCursorInfo(WINCONSOLE.hdl, &WINCONSOLE.save_CI); 1243 T(("... initial cursor is %svisible, %d%%", 1244 (WINCONSOLE.save_CI.bVisible ? "" : "not-"), 1245 (int) WINCONSOLE.save_CI.dwSize)); 1246 } 1247 1248 WINCONSOLE.initialized = TRUE; 1249 console_initialized = TRUE; 1250 } 1251 res = (WINCONSOLE.hdl != INVALID_HANDLE_VALUE); 1252 } 1253 returnBool(res); 1254 } 1255 1256 #endif // _NC_WINDOWS 1257