* 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 <stdio.h>
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()
{
if (fY < fScrollTop || fY > fScrollBottom)
return;
if (fY - fScrollTop > 1) {
fOutput->Blit(0, fScrollTop + 1, fColumns, fY - fScrollTop, 0, fScrollTop);
}
fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr);
}
void
Console::ScrollDown()
{
if (fY < fScrollTop || fY > fScrollBottom)
return;
if (fScrollBottom - fY > 1) {
fOutput->Blit(0, fY, fColumns, fScrollBottom - fY, 0, fY + 1);
}
fOutput->FillGlyph(0, fY, fColumns, 1, ' ', fAttr);
}
void
Console::LineFeed()
{
if (fY == fScrollBottom) {
ScrollUp();
} else if (fY < fScrollBottom) {
fY++;
}
}
void
Console::RLineFeed()
{
if (fY == fScrollTop) {
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) {
argCount++;
args[0] = 0;
}
for (int32 i = 0; i < argCount; i++) {
switch (args[i]) {
case 0:
fAttr = 0x0f;
fBrightAttr = true;
fReverseAttr = false;
break;
case 1:
fBrightAttr = true;
fAttr |= 0x08;
break;
case 2:
fBrightAttr = false;
fAttr &= ~0x08;
break;
case 4:
break;
case 5:
fAttr |= 0x80;
break;
case 7:
fReverseAttr = true;
fAttr = ((fAttr & BMASK) >> 4) | ((fAttr & FMASK) << 4);
if (fBrightAttr)
fAttr |= 0x08;
break;
case 8:
break;
case 30: fAttr = (fAttr & ~FMASK) | 0 | (fBrightAttr ? 0x08 : 0); break;
case 31: fAttr = (fAttr & ~FMASK) | 4 | (fBrightAttr ? 0x08 : 0); break;
case 32: fAttr = (fAttr & ~FMASK) | 2 | (fBrightAttr ? 0x08 : 0); break;
case 33: fAttr = (fAttr & ~FMASK) | 6 | (fBrightAttr ? 0x08 : 0); break;
case 34: fAttr = (fAttr & ~FMASK) | 1 | (fBrightAttr ? 0x08 : 0); break;
case 35: fAttr = (fAttr & ~FMASK) | 5 | (fBrightAttr ? 0x08 : 0); break;
case 36: fAttr = (fAttr & ~FMASK) | 3 | (fBrightAttr ? 0x08 : 0); break;
case 37: fAttr = (fAttr & ~FMASK) | 7 | (fBrightAttr ? 0x08 : 0); break;
case 40: fAttr = (fAttr & ~BMASK) | (0 << 4); break;
case 41: fAttr = (fAttr & ~BMASK) | (4 << 4); break;
case 42: fAttr = (fAttr & ~BMASK) | (2 << 4); break;
case 43: fAttr = (fAttr & ~BMASK) | (6 << 4); break;
case 44: fAttr = (fAttr & ~BMASK) | (1 << 4); break;
case 45: fAttr = (fAttr & ~BMASK) | (5 << 4); break;
case 46: fAttr = (fAttr & ~BMASK) | (3 << 4); break;
case 47: fAttr = (fAttr & ~BMASK) | (7 << 4); break;
}
}
}
bool
Console::ProcessVT100Command(const char c, bool seen_bracket, int32 *args, int32 argCount)
{
bool ret = true;
if (seen_bracket) {
switch(c) {
case 'H':
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': {
int32 deltay = argCount > 0 ? -args[0] : -1;
if (deltay == 0)
deltay = -1;
GotoXY(fX, fY + deltay);
break;
}
case 'e':
case 'B': {
int32 deltay = argCount > 0 ? args[0] : 1;
if (deltay == 0)
deltay = 1;
GotoXY(fX, fY + deltay);
break;
}
case 'D': {
int32 deltax = argCount > 0 ? -args[0] : -1;
if (deltax == 0)
deltax = -1;
GotoXY(fX + deltax, fY);
break;
}
case 'a':
case 'C': {
int32 deltax = argCount > 0 ? args[0] : 1;
if (deltax == 0)
deltax = 1;
GotoXY(fX + deltax, fY);
break;
}
case '`':
case 'G': {
int32 newx = argCount > 0 ? args[0] : 1;
if (newx > 0)
newx--;
GotoXY(newx, fY);
break;
}
case 'd': {
int32 newy = argCount > 0 ? args[0] : 1;
if (newy > 0)
newy--;
GotoXY(fX, newy);
break;
}
case 's':
SaveCursor(false);
break;
case 'u':
RestoreCursor(false);
break;
case 'r': {
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': {
int32 lines = argCount > 0 ? args[0] : 1;
while (lines > 0) {
ScrollDown();
lines--;
}
break;
}
case 'M': {
int32 lines = argCount > 0 ? args[0] : 1;
while (lines > 0) {
ScrollUp();
lines--;
}
break;
}
case 'K':
if (argCount == 0 || args[0] == 0) {
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) {
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);
const char *c;
size_t pos = 0;
while (pos < len) {
c = &((const char *)buf)[pos++];
switch (fState) {
case CONSOLE_STATE_NORMAL:
switch (*c) {
case '\n':
LineFeed();
break;
case '\r':
CariageReturn();
break;
case 0x8:
Delete();
break;
case '\t':
Tab();
break;
case '\a':
break;
case '\0':
break;
case 0x1b:
fArgCount = -1;
fState = CONSOLE_STATE_GOT_ESCAPE;
break;
default:
PutChar(*c);
}
break;
case CONSOLE_STATE_GOT_ESCAPE:
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 '?':
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:
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);
}