/* * Console.cpp - Mimicing the console driver * Based on the console driver. * * Copyright 2005 Michael Lotz. All rights reserved. * Distributed under the MIT License. * * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. * * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. * Distributed under the terms of the NewOS License. */ #include "Console.h" #include "ViewBuffer.h" #include Console::Console(ViewBuffer *output) : fState(CONSOLE_STATE_NORMAL), fOutput(output) { fOutput->GetSize(&fColumns, &fLines); fOutput->SetResizeCallback(&ResizeCallback, this); ResetConsole(); GotoXY(0, 0); SaveCursor(true); fOutput->Clear(0x0f); } Console::~Console() { } void Console::ResetConsole() { fAttr = 0x0f; fScrollTop = 0; fScrollBottom = fLines - 1; fBrightAttr = true; fReverseAttr = false; } void Console::ResizeCallback(int32 width, int32 height, void *data) { Console *console = (Console *)data; console->fColumns = width; console->fLines = height; console->SetScrollRegion(console->fScrollTop, height - 1); } void Console::SetScrollRegion(int top, int bottom) { if (top < 0) top = 0; if (bottom >= fLines) bottom = fLines - 1; if (top > bottom) return; fScrollTop = top; fScrollBottom = bottom; } void Console::ScrollUp() { // see if cursor is outside of scroll region if (fY < fScrollTop || fY > fScrollBottom) return; if (fY - fScrollTop > 1) { // move the screen up one fOutput->Blit(0, fScrollTop + 1, fColumns, fY - fScrollTop, 0, fScrollTop); } // clear the bottom line fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); } void Console::ScrollDown() { // see if cursor is outside of scroll region if (fY < fScrollTop || fY > fScrollBottom) return; if (fScrollBottom - fY > 1) { // move the screen down one fOutput->Blit(0, fY, fColumns, fScrollBottom - fY, 0, fY + 1); } // clear the top line fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); } void Console::LineFeed() { if (fY == fScrollBottom) { // we hit the bottom of our scroll region ScrollUp(); } else if (fY < fScrollBottom) { fY++; } } void Console::RLineFeed() { if (fY == fScrollTop) { // we hit the top of our scroll region ScrollDown(); } else if (fY > fScrollTop) { fY--; } } void Console::CariageReturn() { fX = 0; } void Console::Delete() { if (fX > 0) { fX--; } else if (fY > 0) { fY--; fX = fColumns - 1; } else { ScrollDown(); fY--; fX = fColumns - 1; return; } fOutput->PutGlyph(fX, fY, ' ', fAttr); } void Console::Tab() { fX = (fX + TAB_SIZE) & ~TAB_MASK; if (fX >= fColumns) { fX -= fColumns; LineFeed(); } } void Console::EraseLine(erase_line_mode mode) { switch (mode) { case LINE_ERASE_WHOLE: fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr); break; case LINE_ERASE_LEFT: fOutput->FillGlyph(0, fY, fX + 1, 1, ' ', fAttr); break; case LINE_ERASE_RIGHT: fOutput->FillGlyph(fX, fY, fColumns - fX, 1, ' ', fAttr); break; default: return; } } void Console::EraseScreen(erase_screen_mode mode) { switch (mode) { case SCREEN_ERASE_WHOLE: fOutput->Clear(fAttr); break; case SCREEN_ERASE_UP: fOutput->FillGlyph(0, 0, fColumns, fY + 1, ' ', fAttr); break; case SCREEN_ERASE_DOWN: fOutput->FillGlyph(fY, 0, fColumns, fLines - fY, ' ', fAttr); break; default: return; } } void Console::SaveCursor(bool save_attrs) { fSavedX = fX; fSavedY = fY; if (save_attrs) fSavedAttr = fAttr; } void Console::RestoreCursor(bool restore_attrs) { fX = fSavedX; fY = fSavedY; if (restore_attrs) fAttr = fSavedAttr; } void Console::UpdateCursor(int x, int y) { fOutput->MoveCursor(x, y); } void Console::GotoXY(int new_x, int new_y) { if (new_x >= fColumns) new_x = fColumns - 1; if (new_x < 0) new_x = 0; if (new_y >= fLines) new_y = fLines - 1; if (new_y < 0) new_y = 0; fX = new_x; fY = new_y; } void Console::PutChar(const char c) { fOutput->PutGlyph(fX, fY, c, fAttr); if (++fX >= fColumns) { CariageReturn(); LineFeed(); } } void Console::SetVT100Attributes(int32 *args, int32 argCount) { if (argCount == 0) { // that's the default (attributes off) argCount++; args[0] = 0; } for (int32 i = 0; i < argCount; i++) { switch (args[i]) { case 0: // reset fAttr = 0x0f; fBrightAttr = true; fReverseAttr = false; break; case 1: // bright fBrightAttr = true; fAttr |= 0x08; // set the bright bit break; case 2: // dim fBrightAttr = false; fAttr &= ~0x08; // unset the bright bit break; case 4: // underscore we can't do break; case 5: // blink fAttr |= 0x80; // set the blink bit break; case 7: // reverse fReverseAttr = true; fAttr = ((fAttr & BMASK) >> 4) | ((fAttr & FMASK) << 4); if (fBrightAttr) fAttr |= 0x08; break; case 8: // hidden? break; /* foreground colors */ case 30: fAttr = (fAttr & ~FMASK) | 0 | (fBrightAttr ? 0x08 : 0); break; // black case 31: fAttr = (fAttr & ~FMASK) | 4 | (fBrightAttr ? 0x08 : 0); break; // red case 32: fAttr = (fAttr & ~FMASK) | 2 | (fBrightAttr ? 0x08 : 0); break; // green case 33: fAttr = (fAttr & ~FMASK) | 6 | (fBrightAttr ? 0x08 : 0); break; // yellow case 34: fAttr = (fAttr & ~FMASK) | 1 | (fBrightAttr ? 0x08 : 0); break; // blue case 35: fAttr = (fAttr & ~FMASK) | 5 | (fBrightAttr ? 0x08 : 0); break; // magenta case 36: fAttr = (fAttr & ~FMASK) | 3 | (fBrightAttr ? 0x08 : 0); break; // cyan case 37: fAttr = (fAttr & ~FMASK) | 7 | (fBrightAttr ? 0x08 : 0); break; // white /* background colors */ case 40: fAttr = (fAttr & ~BMASK) | (0 << 4); break; // black case 41: fAttr = (fAttr & ~BMASK) | (4 << 4); break; // red case 42: fAttr = (fAttr & ~BMASK) | (2 << 4); break; // green case 43: fAttr = (fAttr & ~BMASK) | (6 << 4); break; // yellow case 44: fAttr = (fAttr & ~BMASK) | (1 << 4); break; // blue case 45: fAttr = (fAttr & ~BMASK) | (5 << 4); break; // magenta case 46: fAttr = (fAttr & ~BMASK) | (3 << 4); break; // cyan case 47: fAttr = (fAttr & ~BMASK) | (7 << 4); break; // white } } } bool Console::ProcessVT100Command(const char c, bool seen_bracket, int32 *args, int32 argCount) { bool ret = true; if (seen_bracket) { switch(c) { case 'H': /* set cursor position */ case 'f': { int32 row = argCount > 0 ? args[0] : 1; int32 col = argCount > 1 ? args[1] : 1; if (row > 0) row--; if (col > 0) col--; GotoXY(col, row); break; } case 'A': { /* move up */ int32 deltay = argCount > 0 ? -args[0] : -1; if (deltay == 0) deltay = -1; GotoXY(fX, fY + deltay); break; } case 'e': case 'B': { /* move down */ int32 deltay = argCount > 0 ? args[0] : 1; if (deltay == 0) deltay = 1; GotoXY(fX, fY + deltay); break; } case 'D': { /* move left */ int32 deltax = argCount > 0 ? -args[0] : -1; if (deltax == 0) deltax = -1; GotoXY(fX + deltax, fY); break; } case 'a': case 'C': { /* move right */ int32 deltax = argCount > 0 ? args[0] : 1; if (deltax == 0) deltax = 1; GotoXY(fX + deltax, fY); break; } case '`': case 'G': { /* set X position */ int32 newx = argCount > 0 ? args[0] : 1; if (newx > 0) newx--; GotoXY(newx, fY); break; } case 'd': { /* set y position */ int32 newy = argCount > 0 ? args[0] : 1; if (newy > 0) newy--; GotoXY(fX, newy); break; } case 's': /* save current cursor */ SaveCursor(false); break; case 'u': /* restore cursor */ RestoreCursor(false); break; case 'r': { /* set scroll region */ int32 low = argCount > 0 ? args[0] : 1; int32 high = argCount > 1 ? args[1] : fLines; if (low <= high) SetScrollRegion(low - 1, high - 1); break; } case 'L': { /* scroll virtual down at cursor */ int32 lines = argCount > 0 ? args[0] : 1; while (lines > 0) { ScrollDown(); lines--; } break; } case 'M': { /* scroll virtual up at cursor */ int32 lines = argCount > 0 ? args[0] : 1; while (lines > 0) { ScrollUp(); lines--; } break; } case 'K': if (argCount == 0 || args[0] == 0) { // erase to end of line EraseLine(LINE_ERASE_RIGHT); } else if (argCount > 0) { if (args[0] == 1) EraseLine(LINE_ERASE_LEFT); else if (args[0] == 2) EraseLine(LINE_ERASE_WHOLE); } break; case 'J': if (argCount == 0 || args[0] == 0) { // erase to end of screen EraseScreen(SCREEN_ERASE_DOWN); } else if (argCount > 0) { if (args[0] == 1) EraseScreen(SCREEN_ERASE_UP); else if (args[0] == 2) EraseScreen(SCREEN_ERASE_WHOLE); } break; case 'm': if (argCount >= 0) SetVT100Attributes(args, argCount); break; default: ret = false; } } else { switch (c) { case 'c': ResetConsole(); break; case 'D': RLineFeed(); break; case 'M': LineFeed(); break; case '7': SaveCursor(true); break; case '8': RestoreCursor(true); break; default: ret = false; } } return ret; } void Console::Write(const void *buf, size_t len) { UpdateCursor(-1, -1); // hide the cursor const char *c; size_t pos = 0; while (pos < len) { c = &((const char *)buf)[pos++]; switch (fState) { case CONSOLE_STATE_NORMAL: // just output the stuff switch (*c) { case '\n': LineFeed(); break; case '\r': CariageReturn(); break; case 0x8: // backspace Delete(); break; case '\t': Tab(); break; case '\a': // beep //printf("\n"); break; case '\0': break; case 0x1b: // escape character fArgCount = -1; fState = CONSOLE_STATE_GOT_ESCAPE; break; default: PutChar(*c); } break; case CONSOLE_STATE_GOT_ESCAPE: // look for either commands with no argument, or the '[' character switch (*c) { case '[': fState = CONSOLE_STATE_SEEN_BRACKET; break; default: fArgs[fArgCount] = 0; ProcessVT100Command(*c, false, fArgs, fArgCount + 1); fState = CONSOLE_STATE_NORMAL; } break; case CONSOLE_STATE_SEEN_BRACKET: switch (*c) { case '0'...'9': fArgCount = 0; fArgs[fArgCount] = *c - '0'; fState = CONSOLE_STATE_PARSING_ARG; break; case '?': // private DEC mode parameter follows - we ignore those anyway // ToDo: check if it was really used in combination with a mode command break; default: ProcessVT100Command(*c, true, fArgs, fArgCount + 1); fState = CONSOLE_STATE_NORMAL; } break; case CONSOLE_STATE_NEW_ARG: switch (*c) { case '0'...'9': fArgCount++; if (fArgCount == MAX_ARGS) { fState = CONSOLE_STATE_NORMAL; break; } fArgs[fArgCount] = *c - '0'; fState = CONSOLE_STATE_PARSING_ARG; break; default: ProcessVT100Command(*c, true, fArgs, fArgCount + 1); fState = CONSOLE_STATE_NORMAL; } break; case CONSOLE_STATE_PARSING_ARG: // parse args switch (*c) { case '0'...'9': fArgs[fArgCount] *= 10; fArgs[fArgCount] += *c - '0'; break; case ';': fState = CONSOLE_STATE_NEW_ARG; break; default: ProcessVT100Command(*c, true, fArgs, fArgCount + 1); fState = CONSOLE_STATE_NORMAL; } } } UpdateCursor(fX, fY); // show it again }