1 /////////////////////////////////////////////////////////////////////////////// 2 //Telnet Win32 : an ANSI telnet client. 3 //Copyright (C) 1998-2000 Paul Brannan 4 //Copyright (C) 1998 I.Ioannou 5 //Copyright (C) 1997 Brad Johnson 6 // 7 //This program is free software; you can redistribute it and/or 8 //modify it under the terms of the GNU General Public License 9 //as published by the Free Software Foundation; either version 2 10 //of the License, or (at your option) any later version. 11 // 12 //This program is distributed in the hope that it will be useful, 13 //but WITHOUT ANY WARRANTY; without even the implied warranty of 14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 //GNU General Public License for more details. 16 // 17 //You should have received a copy of the GNU General Public License 18 //along with this program; if not, write to the Free Software 19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 // 21 //I.Ioannou 22 //roryt@hol.gr 23 // 24 /////////////////////////////////////////////////////////////////////////// 25 26 /////////////////////////////////////////////////////////////////////////////// 27 // 28 // Module: tconsole.cpp 29 // 30 // Contents: screen functions 31 // 32 // Product: telnet 33 // 34 // 35 // Revisions: Mar. 29, 2000 pbranna@clemson (Paul Brannan) 36 // June 15, 1998 pbranna@clemson.edu 37 // May 16, 1998 pbranna@clemson.edu 38 // 05. Sep.1997 roryt@hol.gr (I.Ioannou) 39 // 11.May,1997 roryt@hol.gr 40 // 06.April,1997 roryt@hol.gr 41 // 30.M�rz.1997 Titus_Boxberg@public.uni-hamburg.de 42 // 5.Dec.1996 jbj@nounname.com 43 // Version 2.0 44 // 02.Apr.1995 igor.milavec@uni-lj.si 45 // Original code 46 // 47 /////////////////////////////////////////////////////////////////////////////// 48 49 #include "precomp.h" 50 51 // argsused doesn't work on MSVC++ 52 #ifdef __BORLANDC__ 53 #pragma argsused 54 #endif 55 56 TConsole::TConsole(HANDLE h) { 57 hConsole = h; 58 59 GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo); 60 61 // Start with correct colors 62 int color_fg = ini.get_normal_fg(); 63 int color_bg = ini.get_normal_bg(); 64 if(color_fg == -1) 65 color_fg = defaultfg = origfg = ConsoleInfo.wAttributes & 0xF; 66 else 67 defaultfg = origfg = color_fg; 68 if(color_bg == -1) 69 color_bg = defaultbg = origbg = (ConsoleInfo.wAttributes >> 4) & 0xF; 70 else 71 defaultbg = origbg = color_bg; 72 wAttributes = color_fg | (color_bg << 4); 73 reverse = blink = underline = false; 74 SetConsoleTextAttribute(hConsole, wAttributes); 75 76 insert_mode = 0; 77 78 // Set the screen size 79 SetWindowSize(ini.get_term_width(), ini.get_term_height()); 80 81 iScrollStart = -1; 82 iScrollEnd = -1; 83 } 84 85 TConsole::~TConsole() { 86 wAttributes = origfg | (origbg << 4); 87 SetCursorPosition(0, CON_HEIGHT); 88 SetConsoleTextAttribute(hConsole, wAttributes); 89 WriteCtrlChar('\x0a'); 90 } 91 92 // Paul Brannan 8/2/98 93 void TConsole::SetWindowSize(int width, int height) { 94 SMALL_RECT sr = { 95 CON_LEFT, 96 CON_TOP, 97 (width == -1) ? CON_RIGHT : CON_LEFT + width - 1, 98 (height == -1) ? CON_BOTTOM : CON_TOP + height - 1 99 }; 100 ConsoleInfo.dwSize.X = width; 101 if(ConsoleInfo.dwSize.Y < height) ConsoleInfo.dwSize.Y = height; 102 SetConsoleScreenBufferSize(hConsole, ConsoleInfo.dwSize); 103 SetConsoleWindowInfo(hConsole, TRUE, &sr); 104 SetConsoleScreenBufferSize(hConsole, ConsoleInfo.dwSize); 105 sync(); 106 } 107 108 // Paul Brannan 5/15/98 109 void TConsole::sync() { 110 GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo); 111 } 112 113 void TConsole::HighVideo() { 114 wAttributes = wAttributes | (unsigned char) 8; 115 } 116 117 void TConsole::LowVideo() { 118 wAttributes = wAttributes & (unsigned char) (0xff-8); 119 } 120 121 void TConsole::Normal() { 122 // I.Ioannou 11 May 1997 123 // Color 7 is correct on some systems (for example Linux) 124 // but not with others (for example SCO) 125 // we must preserve the colors : 126 // 06/04/98 thanks to Paul a .ini parameter from now on 127 128 BlinkOff(); 129 UnderlineOff(); 130 if(ini.get_preserve_colors()) { 131 ReverseOff(); 132 LowVideo(); 133 } else { 134 fg = defaultfg; 135 bg = defaultbg; 136 wAttributes = (unsigned char)fg | (bg << 4); 137 reverse = false; 138 } 139 } 140 141 void TConsole::SetForeground(unsigned char wAttrib) { 142 if(reverse) bg = wAttrib; else fg = wAttrib; 143 wAttributes = (wAttributes & (unsigned char)0x88) | 144 (unsigned char)fg | (bg << 4); 145 } 146 147 void TConsole::SetBackground(unsigned char wAttrib) { 148 if(reverse) fg = wAttrib; else bg = wAttrib; 149 wAttributes = (wAttributes & (unsigned char)0x88) | 150 (unsigned char)fg | (bg << 4); 151 } 152 153 // As far as I can tell, there's no such thing as blink in Windows Console. 154 // I tried using some inline asm to turn off high-intensity backgrounds, 155 // but I got a BSOD. Perhaps there is an undocumented function? 156 // (Paul Brannan 6/27/98) 157 void TConsole::BlinkOn() { 158 blink = 1; 159 if(underline) { 160 UlBlinkOn(); 161 } else { 162 if(ini.get_blink_bg() != -1) { 163 wAttributes &= 0x8f; // turn off bg 164 wAttributes |= ini.get_blink_bg() << 4; 165 } 166 if(ini.get_blink_fg() != -1) { 167 wAttributes &= 0xf8; // turn off fg 168 wAttributes |= ini.get_blink_fg(); 169 } 170 if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1) 171 wAttributes |= 0x80; 172 } 173 } 174 175 // Added by I.Ioannou 06 April, 1997 176 void TConsole::BlinkOff() { 177 blink = 0; 178 if(underline) { 179 UlBlinkOff(); 180 } else { 181 if(ini.get_blink_bg() != -1) { 182 wAttributes &= 0x8f; // turn off bg 183 wAttributes |= defaultbg << 4; 184 } 185 if(ini.get_blink_fg() != -1) { 186 wAttributes &= 0xf8; // turn off fg 187 wAttributes |= defaultfg; 188 } 189 if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1) 190 wAttributes &= 0x7f; 191 } 192 } 193 194 // Paul Brannan 6/27/98 195 void TConsole::UnderlineOn() { 196 underline = 1; 197 if(blink) { 198 UlBlinkOn(); 199 } else { 200 if(ini.get_underline_bg() != -1) { 201 wAttributes &= 0x8f; // turn off bg 202 wAttributes |= ini.get_underline_bg() << 4; 203 } 204 if(ini.get_underline_fg() != -1) { 205 wAttributes &= 0xf8; // turn off fg 206 wAttributes |= ini.get_underline_fg(); 207 } 208 if(ini.get_underline_bg() == -1 && ini.get_underline_fg() == -1) 209 wAttributes |= 0x80; 210 } 211 } 212 213 // Paul Brannan 6/27/98 214 void TConsole::UnderlineOff() { 215 underline = 0; 216 if(blink) { 217 UlBlinkOff(); 218 } else { 219 if(ini.get_blink_bg() != -1) { 220 wAttributes &= 0x8f; // turn off bg 221 wAttributes |= defaultbg << 4; 222 } 223 if(ini.get_blink_fg() != -1) { 224 wAttributes &= 0xf8; // turn off fg 225 wAttributes |= defaultfg; 226 } 227 if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1) 228 wAttributes &= 0x7f; 229 } 230 } 231 232 // Paul Brannan 6/27/98 233 void TConsole::UlBlinkOn() { 234 if(ini.get_ulblink_bg() != -1) { 235 wAttributes &= 0x8f; // turn off bg 236 wAttributes |= ini.get_ulblink_bg() << 4; 237 } 238 if(ini.get_ulblink_fg() != -1) { 239 wAttributes &= 0xf8; // turn off fg 240 wAttributes |= ini.get_ulblink_fg(); 241 } 242 if(ini.get_ulblink_bg() == -1 && ini.get_ulblink_fg() == -1) 243 wAttributes |= 0x80; 244 } 245 246 // Paul Brannan 6/27/98 247 void TConsole::UlBlinkOff() { 248 if(blink) { 249 BlinkOn(); 250 } else if(underline) { 251 UnderlineOn(); 252 } else { 253 Normal(); 254 } 255 } 256 257 // Paul Brannan 6/26/98 258 void TConsole::Lightbg() { 259 WORD *pAttributes = new WORD[CON_COLS]; 260 DWORD Result; 261 262 // Paul Brannan 8/5/98 263 // Correction: processing more than one line at a time causes a segfault 264 // if the screen width != 80 265 for(int i = CON_TOP; i <= CON_BOTTOM; i++) { 266 COORD Coord = {CON_LEFT, i}; 267 268 ReadConsoleOutputAttribute(hConsole, pAttributes, (DWORD)(CON_COLS), 269 Coord, &Result); 270 271 for(DWORD j = 0; j < Result; j++) pAttributes[j] |= 0x80; 272 273 WriteConsoleOutputAttribute(hConsole, pAttributes, Result, Coord, 274 &Result); 275 } 276 277 delete[] pAttributes; // clean up 278 279 wAttributes |= (unsigned char)0x80; 280 bg |= 8; 281 } 282 283 // Paul Brannan 6/26/98 284 void TConsole::Darkbg() { 285 WORD *pAttributes = new WORD[CON_COLS]; 286 DWORD Result; 287 288 // Paul Brannan 8/5/98 289 // Correction: processing more than one line at a time causes a segfault 290 // if the screen width != 80 291 for(int i = CON_TOP; i <= CON_BOTTOM; i++) { 292 COORD Coord = {CON_LEFT, i}; 293 294 ReadConsoleOutputAttribute(hConsole, pAttributes, (DWORD)(CON_COLS), 295 Coord, &Result); 296 297 for(DWORD j = 0; j < Result; j++) pAttributes[j] &= 0x7f; 298 299 WriteConsoleOutputAttribute(hConsole, pAttributes, Result, Coord, 300 &Result); 301 } 302 303 delete[] pAttributes; // clean up 304 305 306 wAttributes &= (unsigned char)0x7f; 307 bg &= 7; 308 } 309 310 // Added by I.Ioannou 11.May,1997 311 void TConsole::ReverseOn() { 312 if (!reverse) { 313 reverse = true; 314 315 // atl : forground attributes without the intensity 316 // ath : backgound attributes without the blink 317 // bl : the blink state 318 // ints : the intensity 319 unsigned char atl = wAttributes & (unsigned char) 0x07; 320 unsigned char ath = wAttributes & (unsigned char) 0x70; 321 unsigned char bl = wAttributes & (unsigned char) 0x80; 322 unsigned char ints = wAttributes & (unsigned char) 0x08; 323 wAttributes = bl | (atl << 4) | ints | (ath >> 4); 324 } 325 } 326 327 // Added by I.Ioannou 11.May,1997 328 void TConsole::ReverseOff() { 329 if (reverse) { 330 reverse = false; 331 wAttributes = fg | (bg << 4); 332 } 333 } 334 335 unsigned long TConsole::WriteText(const char *pszString, unsigned long cbString) { 336 DWORD Result; 337 338 if(insert_mode) { 339 InsertCharacter(cbString); 340 } 341 342 WriteConsoleOutputCharacter(hConsole, (char *)pszString, cbString, 343 ConsoleInfo.dwCursorPosition, &Result); 344 FillConsoleOutputAttribute(hConsole, wAttributes, cbString, 345 ConsoleInfo.dwCursorPosition, &Result); 346 return Result; 347 } 348 349 // Formerly ConWriteString (Paul Brannan 6/28/98) 350 unsigned long TConsole::WriteStringFast(const char* pszString, unsigned long cbString) { 351 DWORD Result; 352 353 SetConsoleTextAttribute(hConsole, wAttributes); 354 355 //check to see if the line is longer than the display 356 if (!getLineWrap() && ((unsigned)CON_CUR_X + cbString) >= (unsigned)CON_COLS) { 357 // Take care of the last line last colum exception... 358 // The display scrolls up if you use the normal char out 359 // function even if you only write to the last place 360 // on the line. :-( 361 if ((unsigned)CON_CUR_Y >= (unsigned)CON_HEIGHT) { 362 unsigned long iFakeResult = cbString; 363 cbString = CON_COLS - CON_CUR_X - 1; 364 365 // FIX ME !!! This will avoid the exception when cbString 366 // is <= 0 but still doesn't work :-( 367 if (cbString > 0) 368 WriteConsole(hConsole, pszString, cbString, &Result, 0); 369 370 COORD dwBufferCoord; 371 dwBufferCoord.X = 0; 372 dwBufferCoord.Y = 0; 373 374 CHAR_INFO ciBuffer; 375 ciBuffer.Char.AsciiChar = *(pszString+cbString); 376 ciBuffer.Attributes = wAttributes; 377 SMALL_RECT srWriteRegion; 378 srWriteRegion.Top = (SHORT) CON_BOTTOM; 379 srWriteRegion.Bottom = (SHORT) CON_BOTTOM; 380 srWriteRegion.Left = (SHORT) CON_RIGHT; 381 srWriteRegion.Right = (SHORT) CON_RIGHT; 382 383 COORD bufSize = {1,1}; 384 385 WriteConsoleOutput(hConsole, &ciBuffer, bufSize, 386 dwBufferCoord, &srWriteRegion); 387 388 // We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98) 389 ConsoleInfo.dwCursorPosition.X = CON_RIGHT; 390 391 return iFakeResult; // Skip the chars that did not fit 392 } 393 // just write the line up to the end 394 else { 395 int iFakeResult = cbString; 396 cbString = CON_COLS - CON_CUR_X; 397 398 if(cbString > 0) { 399 WriteConsole(hConsole, pszString, cbString, &Result, 0); 400 401 // We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98) 402 ConsoleInfo.dwCursorPosition.X += (unsigned short)Result; 403 } 404 405 return iFakeResult; // Skip the chars that did not fit 406 } 407 } else { 408 // If custom scrolling is enabled we must take care of it 409 if(iScrollStart != -1 || iScrollEnd != -1) { 410 return WriteString(pszString, cbString); 411 } 412 413 // Apparently VT100 terminals have an invisible "81st" column that 414 // can hold a cursor until another character is printed. I'm not sure 415 // exactly how to handle this, but here's a hack (Paul Brannan 5/28/98) 416 if(ini.get_vt100_mode() && cbString + (unsigned)CON_CUR_X == (unsigned)CON_COLS) { 417 418 cbString--; 419 if((long)cbString >= 0) WriteConsole(hConsole, pszString, cbString, &Result, 0); 420 421 COORD dwBufferCoord; 422 dwBufferCoord.X = 0; 423 dwBufferCoord.Y = 0; 424 425 CHAR_INFO ciBuffer; 426 ciBuffer.Char.AsciiChar = *(pszString+cbString); 427 ciBuffer.Attributes = wAttributes; 428 SMALL_RECT srWriteRegion; 429 srWriteRegion.Top = (SHORT) ConsoleInfo.dwCursorPosition.Y; 430 srWriteRegion.Bottom = (SHORT) ConsoleInfo.dwCursorPosition.Y; 431 srWriteRegion.Left = (SHORT) CON_RIGHT; 432 srWriteRegion.Right = (SHORT) CON_RIGHT; 433 434 COORD bufSize = {1,1}; 435 436 WriteConsoleOutput(hConsole, &ciBuffer, bufSize, 437 dwBufferCoord, &srWriteRegion); 438 439 // Update the ConsoleInfo struct 440 ConsoleInfo.dwCursorPosition.X = CON_RIGHT + 1; 441 442 return Result + 1; 443 } 444 445 // normal line will wrap normally or not to the end of buffer 446 WriteConsole(hConsole, pszString, cbString, &Result, 0); 447 448 // We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98) 449 // FIX ME!!! This is susceptible to the same problem as above. 450 // (e.g. we write out 160 characters) 451 ConsoleInfo.dwCursorPosition.X += (unsigned short)Result; 452 while(CON_CUR_X > CON_WIDTH) { 453 ConsoleInfo.dwCursorPosition.X -= ConsoleInfo.dwSize.X; 454 if((unsigned)CON_CUR_Y < (unsigned)CON_HEIGHT) { 455 ConsoleInfo.dwCursorPosition.Y++; 456 } else { 457 // If we aren't at the bottom of the window, then we need to 458 // scroll down (Paul Brannan 4/14/2000) 459 if(ConsoleInfo.srWindow.Bottom < ConsoleInfo.dwSize.Y - 1) { 460 ConsoleInfo.srWindow.Top++; 461 ConsoleInfo.srWindow.Bottom++; 462 ConsoleInfo.dwCursorPosition.Y++; 463 SetConsoleWindowInfo(hConsole, TRUE, &ConsoleInfo.srWindow); 464 } 465 } 466 } 467 } 468 469 return Result; 470 471 } 472 473 unsigned long TConsole::WriteString(const char* pszString, unsigned long cbString) { 474 DWORD Result = 0; 475 476 SetConsoleTextAttribute(hConsole, wAttributes); 477 478 //check to see if the line is longer than the display 479 if (!getLineWrap()){ 480 unsigned long iFakeResult = cbString; 481 if((CON_CUR_X + cbString) >= (unsigned int)CON_COLS) 482 cbString = CON_COLS - CON_CUR_X; 483 if(cbString > 0) 484 Result = WriteText(pszString, cbString); 485 486 // We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98) 487 ConsoleInfo.dwCursorPosition.X += (unsigned short)Result; 488 SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition); 489 490 return iFakeResult; // Skip the chars that did not fit 491 } else { 492 // Write up to the end of the line 493 unsigned long temp = cbString; 494 if((CON_CUR_X + temp) > (unsigned int)CON_COLS) { 495 temp = CON_COLS - CON_CUR_X; 496 } else { 497 Result = WriteText(pszString, temp); 498 ConsoleInfo.dwCursorPosition.X += (unsigned short)Result; 499 SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition); 500 return Result; 501 } 502 if(temp > 0) { 503 Result = WriteText(pszString, temp); 504 ConsoleInfo.dwCursorPosition.X += (unsigned short)Result; 505 temp = (unsigned short)Result; 506 } 507 508 // keep writing lines until we get to less than 80 chars left 509 while((temp + (unsigned int)CON_COLS) < cbString) { 510 index(); // LF 511 ConsoleInfo.dwCursorPosition.X = 0; // CR 512 Result = WriteText(&pszString[temp], CON_COLS); 513 temp += (unsigned short)Result; 514 } 515 516 // write out the last bit 517 if(temp < cbString) { 518 index(); 519 ConsoleInfo.dwCursorPosition.X = 0; 520 Result = WriteText(&pszString[temp], cbString - temp); 521 temp += (unsigned short)Result; 522 } 523 524 // Apparently VT100 terminals have an invisible "81st" column that 525 // can hold a cursor until another character is printed. I'm not sure 526 // exactly how to handle this, but here's a hack (Paul Brannan 5/28/98) 527 if(!ini.get_vt100_mode() && cbString + (unsigned)ConsoleInfo.dwCursorPosition.X 528 == (unsigned int)CON_COLS) { 529 index(); 530 ConsoleInfo.dwCursorPosition.X = 0; 531 } 532 533 SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition); 534 535 return temp; 536 } 537 538 return 0; 539 } 540 541 // This is for multi-character control strings (Paul Brannan 6/26/98) 542 unsigned long TConsole::WriteCtrlString(const char *pszString, unsigned long cbString) { 543 unsigned long total = 0; 544 while(total < cbString) { 545 unsigned long Result = WriteCtrlChar(*(pszString + total)); 546 if(Result == 0) { 547 Result = WriteStringFast(pszString + total, 1); 548 if(Result == 0) return total; 549 } 550 total += Result; 551 } 552 return total; 553 } 554 555 // This is for printing single control characters 556 // WriteCtrlString uses this (Paul Brannan 6/26/98) 557 unsigned long TConsole::WriteCtrlChar(char c) { 558 // The console does not handel the CR/LF chars as we might expect 559 // when using color. The attributes are not set correctly, so we 560 // must interpret them manualy to preserve the colors on the screen. 561 562 unsigned long Result = 0; // just in case (Paul Brannan 6/26/98) 563 switch (c) { 564 case '\x09': // horizontal tab 565 SetCursorPosition((((CON_CUR_X/8)+1)*8), CON_CUR_Y); 566 Result = 1; 567 break; 568 569 case '\x0a': // line feed 570 index(); 571 Result = 1; 572 break; 573 case '\x0d': // carrage return 574 SetCursorPosition(CON_LEFT, CON_CUR_Y); // move to beginning of line 575 Result = 1; 576 break; 577 case '\b': // backspace 578 // Added support for backspace so the cursor position can be changed 579 // (Paul Brannan 5/25/98) 580 MoveCursorPosition(-1, 0); 581 Result = 1; 582 default : // else just write it like normal 583 break; 584 } 585 586 return Result; 587 } 588 589 void TConsole::index() { 590 // if on the last line scroll up 591 // This must work with scrolling (Paul Brannan 5/13/98) 592 if(iScrollEnd != -1 && (signed)CON_CUR_Y >= iScrollEnd) { 593 ScrollDown(iScrollStart, iScrollEnd, -1); 594 } else if ((iScrollEnd == -1 && (signed)CON_CUR_Y >= (signed)CON_HEIGHT)) { 595 DWORD Result; 596 WriteConsole(hConsole, "\n", 1, &Result, NULL); 597 598 // If we aren't at the bottom of the buffer, then we need to 599 // scroll down (Paul Brannan 4/14/2000) 600 if(iScrollEnd == -1 && ConsoleInfo.srWindow.Bottom < ConsoleInfo.dwSize.Y - 1) { 601 ConsoleInfo.srWindow.Top++; 602 ConsoleInfo.srWindow.Bottom++; 603 ConsoleInfo.dwCursorPosition.Y++; 604 // SetConsoleWindowInfo(hConsole, TRUE, &ConsoleInfo.srWindow); 605 } else { 606 ClearLine(); 607 } 608 } else { // else move cursor down to the next line 609 SetCursorPosition(CON_CUR_X, CON_CUR_Y + 1); 610 } 611 } 612 613 void TConsole::reverse_index() { 614 // if on the top line scroll down 615 // This must work with scrolling (Paul Brannan 5/13/98) 616 // We should be comparing against iScrollStart, not iScrollEnd (PB 12/2/98) 617 if (iScrollStart == -1 && (signed)CON_CUR_Y <= 0) { 618 ScrollDown(iScrollStart, -1, 1); 619 } else if (iScrollStart != -1 && (signed)CON_CUR_Y <= iScrollStart) { 620 ScrollDown(iScrollStart, iScrollEnd, 1); 621 } else // else move cursor up to the previous line 622 SetCursorPosition(CON_CUR_X,CON_CUR_Y - 1); 623 } 624 625 void TConsole::ScrollDown( int iStartRow , int iEndRow, int bUp ){ 626 CHAR_INFO ciChar; 627 SMALL_RECT srScrollWindow; 628 629 // Correction from I.Ioannou 11 May 1997 630 // check the scroll region 631 if (iStartRow < iScrollStart) iStartRow = iScrollStart; 632 633 // Correction from I.Ioannou 11 May 1997 634 // this will make Top the CON_TOP 635 if ( iStartRow == -1) iStartRow = 0; 636 637 // Correction from I.Ioannou 18 Aug 97 638 if ( iEndRow == -1) { 639 if ( iScrollEnd == -1 ) 640 iEndRow = CON_HEIGHT; 641 else 642 iEndRow = ((CON_HEIGHT <= iScrollEnd) ? CON_HEIGHT : iScrollEnd); 643 } 644 // 645 646 if ( iStartRow > CON_HEIGHT) iStartRow = CON_HEIGHT; 647 if ( iEndRow > CON_HEIGHT) iEndRow = CON_HEIGHT; 648 649 srScrollWindow.Left = (CON_LEFT); 650 srScrollWindow.Right = (SHORT) (CON_RIGHT); 651 srScrollWindow.Top = (SHORT) (CON_TOP + iStartRow ); 652 srScrollWindow.Bottom = (SHORT) (CON_TOP + iEndRow); // don't subtract 1 (PB 5/28) 653 654 ciChar.Char.AsciiChar = ' '; // fill with spaces 655 ciChar.Attributes = wAttributes; // fill with current attrib 656 657 // This should speed things up (Paul Brannan 9/2/98) 658 COORD dwDestOrg = {srScrollWindow.Left, srScrollWindow.Top + bUp}; 659 660 // Note that iEndRow and iStartRow had better not be equal to -1 at this 661 // point. There are four cases to consider for out of bounds. Two of 662 // these cause the scroll window to be cleared; the others cause the 663 // scroll region to be modified. (Paul Brannan 12/3/98) 664 if(dwDestOrg.Y > CON_TOP + iEndRow) { 665 // We are scrolling past the end of the scroll region, so just 666 // clear the window instead (Paul Brannan 12/3/98) 667 ClearWindow(CON_TOP + iStartRow, CON_TOP + iEndRow); 668 return; 669 } else if(dwDestOrg.Y + (iEndRow-iStartRow+1) < CON_TOP + iStartRow) { 670 // We are scrolling past the end of the scroll region, so just 671 // clear the window instead (Paul Brannan 12/3/98) 672 ClearWindow(CON_TOP + iStartRow, CON_TOP + iEndRow); 673 return; 674 } else if(dwDestOrg.Y < CON_TOP + iStartRow) { 675 // Modify the scroll region (Paul Brannan 12/3/98) 676 dwDestOrg.Y = CON_TOP + iStartRow; 677 srScrollWindow.Top -= bUp; 678 } else if(dwDestOrg.Y + (iEndRow-iStartRow+1) > CON_TOP + iEndRow) { 679 // Modify the scroll region (Paul Brannan 12/3/98) 680 srScrollWindow.Bottom -= bUp; 681 } 682 683 ScrollConsoleScreenBuffer(hConsole, &srScrollWindow, 684 0, dwDestOrg, &ciChar); 685 } 686 687 // This allows us to clear the screen with an arbitrary character 688 // (Paul Brannan 6/26/98) 689 void TConsole::ClearScreen(char c) { 690 DWORD dwWritten; 691 COORD Coord = {CON_LEFT, CON_TOP}; 692 FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)* 693 (DWORD)(CON_LINES), Coord, &dwWritten); 694 FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)* 695 (DWORD)(CON_LINES), Coord, &dwWritten); 696 } 697 698 // Same as clear screen, but only affects the scroll region 699 void TConsole::ClearWindow(int iStartRow, int iEndRow, char c) { 700 DWORD dwWritten; 701 COORD Coord = {CON_LEFT, CON_TOP + iStartRow}; 702 FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)* 703 (DWORD)(iEndRow-iStartRow+1), Coord, &dwWritten); 704 FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)* 705 (DWORD)(CON_LINES), Coord, &dwWritten); 706 } 707 708 // Clear from cursor to end of screen 709 void TConsole::ClearEOScreen(char c) 710 { 711 DWORD dwWritten; 712 COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y + 1}; 713 FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)* 714 (DWORD)(CON_HEIGHT - CON_CUR_Y), Coord, &dwWritten); 715 FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)* 716 (DWORD)(CON_LINES - CON_CUR_Y), Coord, &dwWritten); 717 ClearEOLine(); 718 } 719 720 // Clear from beginning of screen to cursor 721 void TConsole::ClearBOScreen(char c) 722 { 723 DWORD dwWritten; 724 COORD Coord = {CON_LEFT, CON_TOP}; 725 FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)* 726 (DWORD)(CON_CUR_Y), Coord, &dwWritten); 727 FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)* 728 (DWORD)(CON_CUR_Y), Coord, &dwWritten); 729 ClearBOLine(); 730 } 731 732 void TConsole::ClearLine(char c) 733 { 734 DWORD dwWritten; 735 COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y}; 736 FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS), 737 Coord, &dwWritten); 738 FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS), 739 Coord, &dwWritten); 740 GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo); 741 } 742 743 void TConsole::ClearEOLine(char c) 744 { 745 DWORD dwWritten; 746 COORD Coord = {CON_LEFT + CON_CUR_X, CON_TOP + CON_CUR_Y}; 747 FillConsoleOutputAttribute(hConsole, wAttributes, 748 (DWORD)(CON_RIGHT - CON_CUR_X) +1, Coord, &dwWritten); 749 FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_RIGHT - CON_CUR_X) +1, 750 Coord, &dwWritten); 751 GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo); 752 } 753 754 void TConsole::ClearBOLine(char c) 755 { 756 DWORD dwWritten; 757 COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y}; 758 FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_CUR_X) + 1, Coord, 759 &dwWritten); 760 FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_CUR_X) + 1, 761 Coord, &dwWritten); 762 GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo); 763 } 764 765 766 // Inserts blank lines to the cursor-y-position 767 // scrolls down the rest. CURSOR MOVEMENT (to Col#1) ??? 768 void TConsole::InsertLine(int numlines) 769 { 770 COORD to; 771 SMALL_RECT from; 772 SMALL_RECT clip; 773 CHAR_INFO fill; 774 int acty; 775 776 // Rest of screen would be deleted 777 if ( (acty = GetCursorY()) >= CON_LINES - numlines ) { 778 ClearEOScreen(); // delete rest of screen 779 return; 780 } /* IF */ 781 782 // Else scroll down the part of the screen which is below the 783 // cursor. 784 from.Left = CON_LEFT; 785 from.Top = CON_TOP + (SHORT)acty; 786 from.Right = CON_LEFT + (SHORT)CON_COLS; 787 from.Bottom = CON_TOP + (SHORT)CON_LINES; 788 789 clip = from; 790 to.X = 0; 791 to.Y = (SHORT)(from.Top + numlines); 792 793 fill.Char.AsciiChar = ' '; 794 fill.Attributes = 7; // WHICH ATTRIBUTES TO TAKE FOR BLANK LINE ?? 795 796 ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill); 797 } /* InsertLine */ 798 799 // Inserts blank characters under the cursor 800 void TConsole::InsertCharacter(int numchar) 801 { 802 int actx; 803 SMALL_RECT from; 804 SMALL_RECT clip; 805 COORD to; 806 CHAR_INFO fill; 807 808 if ( (actx = GetCursorX()) >= CON_COLS - numchar ) { 809 ClearEOLine(); 810 return; 811 } /* IF */ 812 813 from.Left = CON_LEFT + (SHORT)actx; 814 from.Top = CON_TOP + (SHORT)GetCursorY(); 815 from.Right = CON_LEFT + (SHORT)CON_COLS; 816 from.Bottom = CON_TOP + (SHORT)from.Top; 817 818 clip = from; 819 to.X = (SHORT)(actx + numchar); 820 to.Y = from.Top; 821 822 fill.Char.AsciiChar = ' '; 823 fill.Attributes = wAttributes; // WHICH ATTRIBUTES TO TAKE FOR BLANK CHAR ?? 824 825 ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill); 826 } /* InsertCharacter */ 827 828 // Deletes characters under the cursor 829 // Note that there are cases in which all the following lines should shift by 830 // a character, but we don't handle these. This could break some 831 // VT102-applications, but it shouldn't be too much of an issue. 832 void TConsole::DeleteCharacter(int numchar) 833 { 834 int actx; 835 SMALL_RECT from; 836 SMALL_RECT clip; 837 COORD to; 838 CHAR_INFO fill; 839 840 if ( (actx = GetCursorX()) >= CON_COLS - numchar ) { 841 ClearEOLine(); 842 return; 843 } /* IF */ 844 845 from.Left = CON_LEFT + (SHORT)actx; 846 from.Top = CON_TOP + (SHORT)GetCursorY(); 847 from.Right = CON_LEFT + (SHORT)CON_COLS; 848 from.Bottom = CON_TOP + from.Top; 849 850 clip = from; 851 to.X = (SHORT)(actx - numchar); 852 to.Y = from.Top; 853 854 fill.Char.AsciiChar = ' '; 855 fill.Attributes = wAttributes; // WHICH ATTRIBUTES TO TAKE FOR BLANK CHAR ?? 856 857 ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill); 858 } /* DeleteCharacter */ 859 860 void TConsole::SetRawCursorPosition(int x, int y) { 861 if (x > CON_WIDTH) x = CON_WIDTH; 862 if (x < 0) x = 0; 863 if (y > CON_HEIGHT) y = CON_HEIGHT; 864 if (y < 0) y = 0; 865 COORD Coord = {(short)(CON_LEFT + x), (short)(CON_TOP + y)}; 866 SetConsoleCursorPosition(hConsole, Coord); 867 868 // Update the ConsoleInfo struct (Paul Brannan 5/9/98) 869 ConsoleInfo.dwCursorPosition.Y = Coord.Y; 870 ConsoleInfo.dwCursorPosition.X = Coord.X; 871 872 // bug fix in case we went too far (Paul Brannan 5/25/98) 873 if(ConsoleInfo.dwCursorPosition.X < CON_LEFT) 874 ConsoleInfo.dwCursorPosition.X = CON_LEFT; 875 if(ConsoleInfo.dwCursorPosition.X > CON_RIGHT) 876 ConsoleInfo.dwCursorPosition.X = CON_RIGHT; 877 if(ConsoleInfo.dwCursorPosition.Y < CON_TOP) 878 ConsoleInfo.dwCursorPosition.Y = CON_TOP; 879 if(ConsoleInfo.dwCursorPosition.Y > CON_BOTTOM) 880 ConsoleInfo.dwCursorPosition.Y = CON_BOTTOM; 881 } 882 883 // The new SetCursorPosition takes scroll regions into consideration 884 // (Paul Brannan 6/27/98) 885 void TConsole::SetCursorPosition(int x, int y) { 886 if (x > CON_WIDTH) x = CON_WIDTH; 887 if (x < 0) x = 0; 888 if(iScrollEnd != -1) { 889 if(y > iScrollEnd) y = iScrollEnd; 890 } else { 891 if(y > CON_HEIGHT) y = CON_HEIGHT; 892 } 893 if(iScrollStart != -1) { 894 if(y < iScrollStart) y = iScrollStart; 895 } else { 896 if(y < 0) y = 0; 897 } 898 899 COORD Coord = {(short)(CON_LEFT + x), (short)(CON_TOP + y)}; 900 SetConsoleCursorPosition(hConsole, Coord); 901 902 // Update the ConsoleInfo struct 903 ConsoleInfo.dwCursorPosition.Y = Coord.Y; 904 ConsoleInfo.dwCursorPosition.X = Coord.X; 905 } 906 907 void TConsole::MoveCursorPosition(int x, int y) { 908 SetCursorPosition(CON_CUR_X + x, CON_CUR_Y + y); 909 } 910 911 void TConsole::SetExtendedMode(int iFunction, BOOL bEnable) 912 { 913 // Probably should do something here... 914 // Should change the screen mode, but do we need this? 915 } 916 917 void TConsole::SetScroll(int start, int end) { 918 iScrollStart = start; 919 iScrollEnd = end; 920 } 921 922 void TConsole::Beep() { 923 if(ini.get_do_beep()) { 924 if(!ini.get_speaker_beep()) printit("\a"); 925 else ::Beep(400, 100); 926 } 927 } 928 929 void TConsole::SetCursorSize(int pct) { 930 CONSOLE_CURSOR_INFO ci = {(pct != 0)?pct:1, pct != 0}; 931 SetConsoleCursorInfo(hConsole, &ci); 932 } 933 934 void saveScreen(CHAR_INFO *chiBuffer) { 935 HANDLE hStdout; 936 CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; 937 SMALL_RECT srctReadRect; 938 COORD coordBufSize; 939 COORD coordBufCoord; 940 941 hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 942 GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo); 943 944 srctReadRect.Top = CON_TOP; /* top left: row 0, col 0 */ 945 srctReadRect.Left = CON_LEFT; 946 srctReadRect.Bottom = CON_BOTTOM; /* bot. right: row 1, col 79 */ 947 srctReadRect.Right = CON_RIGHT; 948 949 coordBufSize.Y = CON_BOTTOM-CON_TOP+1; 950 coordBufSize.X = CON_RIGHT-CON_LEFT+1; 951 952 coordBufCoord.X = CON_TOP; 953 coordBufCoord.Y = CON_LEFT; 954 955 ReadConsoleOutput( 956 hStdout, /* screen buffer to read from */ 957 chiBuffer, /* buffer to copy into */ 958 coordBufSize, /* col-row size of chiBuffer */ 959 960 coordBufCoord, /* top left dest. cell in chiBuffer */ 961 &srctReadRect); /* screen buffer source rectangle */ 962 } 963 964 void restoreScreen(CHAR_INFO *chiBuffer) { 965 HANDLE hStdout; 966 CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; 967 SMALL_RECT srctReadRect; 968 COORD coordBufSize; 969 COORD coordBufCoord; 970 971 hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 972 GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo); 973 974 // restore screen 975 srctReadRect.Top = CON_TOP; /* top left: row 0, col 0 */ 976 srctReadRect.Left = CON_LEFT; 977 srctReadRect.Bottom = CON_BOTTOM; /* bot. right: row 1, col 79 */ 978 srctReadRect.Right = CON_RIGHT; 979 980 coordBufSize.Y = CON_BOTTOM-CON_TOP+1; 981 coordBufSize.X = CON_RIGHT-CON_LEFT+1; 982 983 coordBufCoord.X = CON_TOP; 984 coordBufCoord.Y = CON_LEFT; 985 WriteConsoleOutput( 986 hStdout, /* screen buffer to write to */ 987 chiBuffer, /* buffer to copy from */ 988 coordBufSize, /* col-row size of chiBuffer */ 989 coordBufCoord, /* top left src cell in chiBuffer */ 990 &srctReadRect); /* dest. screen buffer rectangle */ 991 // end restore screen 992 993 } 994 995 CHAR_INFO* newBuffer() { 996 CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; 997 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 998 GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo); 999 CHAR_INFO * chiBuffer; 1000 chiBuffer = new CHAR_INFO[(CON_BOTTOM-CON_TOP+1)*(CON_RIGHT-CON_LEFT+1)]; 1001 return chiBuffer; 1002 } 1003 1004 void deleteBuffer(CHAR_INFO* chiBuffer) { 1005 delete[] chiBuffer; 1006 } 1007 1008