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