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