1 /* 2 * Console.cpp - Mimicing the console driver 3 * Based on the console driver. 4 * 5 * Copyright 2005 Michael Lotz. All rights reserved. 6 * Distributed under the Haiku License. 7 * 8 * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 9 * Distributed under the terms of the MIT License. 10 * 11 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 12 * Distributed under the terms of the NewOS License. 13 */ 14 15 #include "Console.h" 16 #include "ViewBuffer.h" 17 #include <stdio.h> 18 19 Console::Console(ViewBuffer *output) 20 : fOutput(output) 21 { 22 fOutput->GetSize(&fColumns, &fLines); 23 fOutput->SetResizeCallback(&ResizeCallback, this); 24 ResetConsole(); 25 GotoXY(0, 0); 26 SaveCursor(true); 27 fOutput->Clear(0x0f); 28 } 29 30 31 Console::~Console() 32 { 33 } 34 35 36 void 37 Console::ResetConsole() 38 { 39 fAttr = 0x0f; 40 fScrollTop = 0; 41 fScrollBottom = fLines - 1; 42 fBrightAttr = true; 43 fReverseAttr = false; 44 } 45 46 47 void 48 Console::ResizeCallback(int32 width, int32 height, void *data) 49 { 50 Console *console = (Console *)data; 51 52 console->fColumns = width; 53 console->fLines = height; 54 console->SetScrollRegion(console->fScrollTop, height - 1); 55 } 56 57 58 void 59 Console::SetScrollRegion(int top, int bottom) 60 { 61 if (top < 0) 62 top = 0; 63 if (bottom >= fLines) 64 bottom = fLines - 1; 65 if (top > bottom) 66 return; 67 68 fScrollTop = top; 69 fScrollBottom = bottom; 70 } 71 72 73 void 74 Console::ScrollUp() 75 { 76 // see if cursor is outside of scroll region 77 if (fY < fScrollTop || fY > fScrollBottom) 78 return; 79 80 if (fY - fScrollTop > 1) { 81 // move the screen up one 82 fOutput->Blit(0, fScrollTop + 1, fColumns, fY - fScrollTop, 0, fScrollTop); 83 } 84 85 // clear the bottom line 86 fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); 87 } 88 89 90 void 91 Console::ScrollDown() 92 { 93 // see if cursor is outside of scroll region 94 if (fY < fScrollTop || fY > fScrollBottom) 95 return; 96 97 if (fScrollBottom - fY > 1) { 98 // move the screen down one 99 fOutput->Blit(0, fY, fColumns, fScrollBottom - fY, 0, fY + 1); 100 } 101 102 // clear the top line 103 fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); 104 } 105 106 107 void 108 Console::LineFeed() 109 { 110 if (fY == fScrollBottom) { 111 // we hit the bottom of our scroll region 112 ScrollUp(); 113 } else if (fY < fScrollBottom) { 114 fY++; 115 } 116 } 117 118 119 void 120 Console::RLineFeed() 121 { 122 if (fY == fScrollTop) { 123 // we hit the top of our scroll region 124 ScrollDown(); 125 } else if (fY > fScrollTop) { 126 fY--; 127 } 128 } 129 130 131 void 132 Console::CariageReturn() 133 { 134 fX = 0; 135 } 136 137 138 void 139 Console::Delete() 140 { 141 if (fX > 0) { 142 fX--; 143 } else if (fY > 0) { 144 fY--; 145 fX = fColumns - 1; 146 } else { 147 ScrollDown(); 148 fY--; 149 fX = fColumns - 1; 150 return; 151 } 152 153 fOutput->PutGlyph(fX, fY, ' ', fAttr); 154 } 155 156 157 void 158 Console::Tab() 159 { 160 fX = (fX + TAB_SIZE) & ~TAB_MASK; 161 if (fX >= fColumns) { 162 fX -= fColumns; 163 LineFeed(); 164 } 165 } 166 167 168 void 169 Console::EraseLine(erase_line_mode mode) 170 { 171 switch (mode) { 172 case LINE_ERASE_WHOLE: 173 fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); 174 break; 175 case LINE_ERASE_LEFT: 176 fOutput->FillGlyph(0, fY, fX + 1, 1, ' ', fAttr); 177 break; 178 case LINE_ERASE_RIGHT: 179 fOutput->FillGlyph(fX, fY, fColumns - fX, 1, ' ', fAttr); 180 break; 181 default: 182 return; 183 } 184 } 185 186 187 void 188 Console::EraseScreen(erase_screen_mode mode) 189 { 190 switch (mode) { 191 case SCREEN_ERASE_WHOLE: 192 fOutput->Clear(fAttr); 193 break; 194 case SCREEN_ERASE_UP: 195 fOutput->FillGlyph(0, 0, fColumns, fY + 1, ' ', fAttr); 196 break; 197 case SCREEN_ERASE_DOWN: 198 fOutput->FillGlyph(fY, 0, fColumns, fLines - fY, ' ', fAttr); 199 break; 200 default: 201 return; 202 } 203 } 204 205 206 void 207 Console::SaveCursor(bool save_attrs) 208 { 209 fSavedX = fX; 210 fSavedY = fY; 211 212 if (save_attrs) 213 fSavedAttr = fAttr; 214 } 215 216 217 void 218 Console::RestoreCursor(bool restore_attrs) 219 { 220 fX = fSavedX; 221 fY = fSavedY; 222 223 if (restore_attrs) 224 fAttr = fSavedAttr; 225 } 226 227 228 void 229 Console::UpdateCursor(int x, int y) 230 { 231 fOutput->MoveCursor(x, y); 232 } 233 234 235 void 236 Console::GotoXY(int new_x, int new_y) 237 { 238 if (new_x >= fColumns) 239 new_x = fColumns - 1; 240 if (new_x < 0) 241 new_x = 0; 242 if (new_y >= fLines) 243 new_y = fLines - 1; 244 if (new_y < 0) 245 new_y = 0; 246 247 fX = new_x; 248 fY = new_y; 249 } 250 251 252 void 253 Console::PutChar(const char c) 254 { 255 fOutput->PutGlyph(fX, fY, c, fAttr); 256 if (++fX >= fColumns) { 257 CariageReturn(); 258 LineFeed(); 259 } 260 } 261 262 263 void 264 Console::SetVT100Attributes(int32 *args, int32 argCount) 265 { 266 if (argCount == 0) { 267 // that's the default (attributes off) 268 argCount++; 269 args[0] = 0; 270 } 271 272 for (int32 i = 0; i < argCount; i++) { 273 switch (args[i]) { 274 case 0: // reset 275 fAttr = 0x0f; 276 fBrightAttr = true; 277 fReverseAttr = false; 278 break; 279 case 1: // bright 280 fBrightAttr = true; 281 fAttr |= 0x08; // set the bright bit 282 break; 283 case 2: // dim 284 fBrightAttr = false; 285 fAttr &= ~0x08; // unset the bright bit 286 break; 287 case 4: // underscore we can't do 288 break; 289 case 5: // blink 290 fAttr |= 0x80; // set the blink bit 291 break; 292 case 7: // reverse 293 fReverseAttr = true; 294 fAttr = ((fAttr & BMASK) >> 4) | ((fAttr & FMASK) << 4); 295 if (fBrightAttr) 296 fAttr |= 0x08; 297 break; 298 case 8: // hidden? 299 break; 300 301 /* foreground colors */ 302 case 30: fAttr = (fAttr & ~FMASK) | 0 | (fBrightAttr ? 0x08 : 0); break; // black 303 case 31: fAttr = (fAttr & ~FMASK) | 4 | (fBrightAttr ? 0x08 : 0); break; // red 304 case 32: fAttr = (fAttr & ~FMASK) | 2 | (fBrightAttr ? 0x08 : 0); break; // green 305 case 33: fAttr = (fAttr & ~FMASK) | 6 | (fBrightAttr ? 0x08 : 0); break; // yellow 306 case 34: fAttr = (fAttr & ~FMASK) | 1 | (fBrightAttr ? 0x08 : 0); break; // blue 307 case 35: fAttr = (fAttr & ~FMASK) | 5 | (fBrightAttr ? 0x08 : 0); break; // magenta 308 case 36: fAttr = (fAttr & ~FMASK) | 3 | (fBrightAttr ? 0x08 : 0); break; // cyan 309 case 37: fAttr = (fAttr & ~FMASK) | 7 | (fBrightAttr ? 0x08 : 0); break; // white 310 311 /* background colors */ 312 case 40: fAttr = (fAttr & ~BMASK) | (0 << 4); break; // black 313 case 41: fAttr = (fAttr & ~BMASK) | (4 << 4); break; // red 314 case 42: fAttr = (fAttr & ~BMASK) | (2 << 4); break; // green 315 case 43: fAttr = (fAttr & ~BMASK) | (6 << 4); break; // yellow 316 case 44: fAttr = (fAttr & ~BMASK) | (1 << 4); break; // blue 317 case 45: fAttr = (fAttr & ~BMASK) | (5 << 4); break; // magenta 318 case 46: fAttr = (fAttr & ~BMASK) | (3 << 4); break; // cyan 319 case 47: fAttr = (fAttr & ~BMASK) | (7 << 4); break; // white 320 } 321 } 322 } 323 324 325 bool 326 Console::ProcessVT100Command(const char c, bool seen_bracket, int32 *args, int32 argCount) 327 { 328 bool ret = true; 329 330 if (seen_bracket) { 331 switch(c) { 332 case 'H': /* set cursor position */ 333 case 'f': { 334 int32 row = argCount > 0 ? args[0] : 1; 335 int32 col = argCount > 1 ? args[1] : 1; 336 if (row > 0) 337 row--; 338 if (col > 0) 339 col--; 340 GotoXY(col, row); 341 break; 342 } 343 case 'A': { /* move up */ 344 int32 deltay = argCount > 0 ? -args[0] : -1; 345 if (deltay == 0) 346 deltay = -1; 347 GotoXY(fX, fY + deltay); 348 break; 349 } 350 case 'e': 351 case 'B': { /* move down */ 352 int32 deltay = argCount > 0 ? args[0] : 1; 353 if (deltay == 0) 354 deltay = 1; 355 GotoXY(fX, fY + deltay); 356 break; 357 } 358 case 'D': { /* move left */ 359 int32 deltax = argCount > 0 ? -args[0] : -1; 360 if (deltax == 0) 361 deltax = -1; 362 GotoXY(fX + deltax, fY); 363 break; 364 } 365 case 'a': 366 case 'C': { /* move right */ 367 int32 deltax = argCount > 0 ? args[0] : 1; 368 if (deltax == 0) 369 deltax = 1; 370 GotoXY(fX + deltax, fY); 371 break; 372 } 373 case '`': 374 case 'G': { /* set X position */ 375 int32 newx = argCount > 0 ? args[0] : 1; 376 if (newx > 0) 377 newx--; 378 GotoXY(newx, fY); 379 break; 380 } 381 case 'd': { /* set y position */ 382 int32 newy = argCount > 0 ? args[0] : 1; 383 if (newy > 0) 384 newy--; 385 GotoXY(fX, newy); 386 break; 387 } 388 case 's': /* save current cursor */ 389 SaveCursor(false); 390 break; 391 case 'u': /* restore cursor */ 392 RestoreCursor(false); 393 break; 394 case 'r': { /* set scroll region */ 395 int32 low = argCount > 0 ? args[0] : 1; 396 int32 high = argCount > 1 ? args[1] : fLines; 397 if (low <= high) 398 SetScrollRegion(low - 1, high - 1); 399 break; 400 } 401 case 'L': { /* scroll virtual down at cursor */ 402 int32 lines = argCount > 0 ? args[0] : 1; 403 while (lines > 0) { 404 ScrollDown(); 405 lines--; 406 } 407 break; 408 } 409 case 'M': { /* scroll virtual up at cursor */ 410 int32 lines = argCount > 0 ? args[0] : 1; 411 while (lines > 0) { 412 ScrollUp(); 413 lines--; 414 } 415 break; 416 } 417 case 'K': 418 if (argCount == 0 || args[0] == 0) { 419 // erase to end of line 420 EraseLine(LINE_ERASE_RIGHT); 421 } else if (argCount > 0) { 422 if (args[0] == 1) 423 EraseLine(LINE_ERASE_LEFT); 424 else if (args[0] == 2) 425 EraseLine(LINE_ERASE_WHOLE); 426 } 427 break; 428 case 'J': 429 if (argCount == 0 || args[0] == 0) { 430 // erase to end of screen 431 EraseScreen(SCREEN_ERASE_DOWN); 432 } else if (argCount > 0) { 433 if (args[0] == 1) 434 EraseScreen(SCREEN_ERASE_UP); 435 else if (args[0] == 2) 436 EraseScreen(SCREEN_ERASE_WHOLE); 437 } 438 break; 439 case 'm': 440 if (argCount >= 0) 441 SetVT100Attributes(args, argCount); 442 break; 443 default: 444 ret = false; 445 } 446 } else { 447 switch (c) { 448 case 'c': 449 ResetConsole(); 450 break; 451 case 'D': 452 RLineFeed(); 453 break; 454 case 'M': 455 LineFeed(); 456 break; 457 case '7': 458 SaveCursor(true); 459 break; 460 case '8': 461 RestoreCursor(true); 462 break; 463 default: 464 ret = false; 465 } 466 } 467 468 return ret; 469 } 470 471 472 void 473 Console::Write(const void *buf, size_t len) 474 { 475 UpdateCursor(-1, -1); // hide the cursor 476 477 const char *c; 478 size_t pos = 0; 479 480 while (pos < len) { 481 c = &((const char *)buf)[pos++]; 482 483 switch (fState) { 484 case CONSOLE_STATE_NORMAL: 485 // just output the stuff 486 switch (*c) { 487 case '\n': 488 LineFeed(); 489 break; 490 case '\r': 491 CariageReturn(); 492 break; 493 case 0x8: // backspace 494 Delete(); 495 break; 496 case '\t': 497 Tab(); 498 break; 499 case '\a': 500 // beep 501 //printf("<BEEP>\n"); 502 break; 503 case '\0': 504 break; 505 case 0x1b: 506 // escape character 507 fArgCount = -1; 508 fState = CONSOLE_STATE_GOT_ESCAPE; 509 break; 510 default: 511 PutChar(*c); 512 } 513 break; 514 case CONSOLE_STATE_GOT_ESCAPE: 515 // look for either commands with no argument, or the '[' character 516 switch (*c) { 517 case '[': 518 fState = CONSOLE_STATE_SEEN_BRACKET; 519 break; 520 default: 521 fArgs[fArgCount] = 0; 522 ProcessVT100Command(*c, false, fArgs, fArgCount + 1); 523 fState = CONSOLE_STATE_NORMAL; 524 } 525 break; 526 case CONSOLE_STATE_SEEN_BRACKET: 527 switch (*c) { 528 case '0'...'9': 529 fArgCount = 0; 530 fArgs[fArgCount] = *c - '0'; 531 fState = CONSOLE_STATE_PARSING_ARG; 532 break; 533 case '?': 534 // private DEC mode parameter follows - we ignore those anyway 535 // ToDo: check if it was really used in combination with a mode command 536 break; 537 default: 538 ProcessVT100Command(*c, true, fArgs, fArgCount + 1); 539 fState = CONSOLE_STATE_NORMAL; 540 } 541 break; 542 case CONSOLE_STATE_NEW_ARG: 543 switch (*c) { 544 case '0'...'9': 545 fArgCount++; 546 if (fArgCount == MAX_ARGS) { 547 fState = CONSOLE_STATE_NORMAL; 548 break; 549 } 550 fArgs[fArgCount] = *c - '0'; 551 fState = CONSOLE_STATE_PARSING_ARG; 552 break; 553 default: 554 ProcessVT100Command(*c, true, fArgs, fArgCount + 1); 555 fState = CONSOLE_STATE_NORMAL; 556 } 557 break; 558 case CONSOLE_STATE_PARSING_ARG: 559 // parse args 560 switch (*c) { 561 case '0'...'9': 562 fArgs[fArgCount] *= 10; 563 fArgs[fArgCount] += *c - '0'; 564 break; 565 case ';': 566 fState = CONSOLE_STATE_NEW_ARG; 567 break; 568 default: 569 ProcessVT100Command(*c, true, fArgs, fArgCount + 1); 570 fState = CONSOLE_STATE_NORMAL; 571 } 572 } 573 } 574 575 UpdateCursor(fX, fY); // show it again 576 } 577