mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
The console can now be rendered to any text buffer.
The console renderer now renders to a text buffer, which can be implemented on any device, whether it's the VGA text buffer or a bitmap graphics device with font rendering. This replaces the older code that could only render to a VGA framebuffer and where the input parsing was tightly coupled with the device rendering phase.
This commit is contained in:
parent
8c5ab54c9b
commit
f5c4b64aff
11 changed files with 959 additions and 595 deletions
|
@ -110,8 +110,10 @@ uart.o \
|
|||
terminal.o \
|
||||
linebuffer.o \
|
||||
logterminal.o \
|
||||
vgaterminal.o \
|
||||
textterminal.o \
|
||||
serialterminal.o \
|
||||
textbuffer.o \
|
||||
vgatextbuffer.o \
|
||||
descriptors.o \
|
||||
device.o \
|
||||
refcount.o \
|
||||
|
|
99
sortix/include/sortix/kernel/textbuffer.h
Normal file
99
sortix/include/sortix/kernel/textbuffer.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
textbuffer.h
|
||||
Provides a indexable text buffer for used by text mode terminals.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_TEXTBUFFER_H
|
||||
#define SORTIX_TEXTBUFFER_H
|
||||
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
struct TextPos
|
||||
{
|
||||
TextPos() { }
|
||||
TextPos(size_t x, size_t y) : x(x), y(y) { }
|
||||
size_t x;
|
||||
size_t y;
|
||||
};
|
||||
|
||||
struct TextChar
|
||||
{
|
||||
TextChar() { }
|
||||
TextChar(char c, uint8_t vgacolor) : c(c), vgacolor(vgacolor) { }
|
||||
char c;
|
||||
uint8_t vgacolor; // Format of <sortix/vga.h>
|
||||
};
|
||||
|
||||
class TextBuffer
|
||||
{
|
||||
public:
|
||||
virtual ~TextBuffer() { }
|
||||
virtual size_t Width() const = 0;
|
||||
virtual size_t Height() const = 0;
|
||||
virtual TextChar GetChar(TextPos pos) const = 0;
|
||||
virtual void SetChar(TextPos pos, TextChar c) = 0;
|
||||
virtual uint16_t GetCharAttr(TextPos pos) const = 0;
|
||||
virtual void SetCharAttr(TextPos pos, uint16_t attr) = 0;
|
||||
virtual void Scroll(ssize_t off, TextChar fillwith) = 0;
|
||||
virtual void Move(TextPos to, TextPos from, size_t numchars) = 0;
|
||||
virtual void Fill(TextPos from, TextPos to, TextChar fillwith,
|
||||
uint16_t fillattr) = 0;
|
||||
virtual bool GetCursorEnabled() const = 0;
|
||||
virtual void SetCursorEnabled(bool enablecursor) = 0;
|
||||
virtual TextPos GetCursorPos() const = 0;
|
||||
virtual void SetCursorPos(TextPos cursorpos) = 0;
|
||||
|
||||
};
|
||||
|
||||
// The purpose of this handle class is such that the terminal driver can have
|
||||
// its backing storage replaced at runtime, for instance if the user changes
|
||||
// the screen resolution or the graphics driver. The backing text buffer can
|
||||
// only be changed when there are no references (but our own) to the text buffer
|
||||
// so don't forget to release it when you are done.
|
||||
class TextBufferHandle : public Refcounted
|
||||
{
|
||||
public:
|
||||
TextBufferHandle(TextBuffer* textbuf = NULL, bool deletebuf = true,
|
||||
TextBuffer* def = NULL, bool deletedef = true);
|
||||
~TextBufferHandle();
|
||||
TextBuffer* Acquire();
|
||||
void Release(TextBuffer* textbuf);
|
||||
void Replace(TextBuffer* newtextbuf, bool deletebuf = true);
|
||||
|
||||
private:
|
||||
kthread_mutex_t mutex;
|
||||
kthread_cond_t unusedcond;
|
||||
TextBuffer* textbuf;
|
||||
TextBuffer* def;
|
||||
size_t numused;
|
||||
bool deletedef;
|
||||
bool deletebuf;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
|
@ -24,6 +24,9 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include <libmaxsi/string.h>
|
||||
#include <libmaxsi/format.h>
|
||||
|
@ -42,9 +45,10 @@
|
|||
#include "pci.h"
|
||||
#include "com.h"
|
||||
#include "uart.h"
|
||||
#include "vgatextbuffer.h"
|
||||
#include "terminal.h"
|
||||
#include "serialterminal.h"
|
||||
#include "vgaterminal.h"
|
||||
#include "textterminal.h"
|
||||
#include "elf.h"
|
||||
#include "initrd.h"
|
||||
#include "vga.h"
|
||||
|
@ -90,6 +94,11 @@ void DoWelcome()
|
|||
Log::Print(" BOOTING OPERATING SYSTEM... ");
|
||||
}
|
||||
|
||||
static size_t PrintToTextTerminal(void* user, const char* str, size_t len)
|
||||
{
|
||||
return ((TextTerminal*) user)->Print(str, len);
|
||||
}
|
||||
|
||||
extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
||||
{
|
||||
// Initialize system calls.
|
||||
|
@ -98,13 +107,19 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo)
|
|||
// Detect and initialize any serial COM ports in the system.
|
||||
COM::EarlyInit();
|
||||
|
||||
// Initialize the default terminal.
|
||||
VGATerminal::Init();
|
||||
Maxsi::Format::Callback logcallback = VGATerminal::Print;
|
||||
void* logpointer = NULL;
|
||||
// Setup a text buffer handle for use by the text terminal.
|
||||
uint16_t* const VGAFB = (uint16_t*) 0xB8000;
|
||||
const size_t VGA_WIDTH = 80;
|
||||
const size_t VGA_HEIGHT = 25;
|
||||
static uint16_t vga_attr_buffer[VGA_WIDTH*VGA_HEIGHT];
|
||||
VGATextBuffer textbuf(VGAFB, vga_attr_buffer, VGA_WIDTH, VGA_HEIGHT);
|
||||
TextBufferHandle textbufhandle(NULL, false, &textbuf, false);
|
||||
|
||||
// Initialize the kernel log.
|
||||
Log::Init(logcallback, logpointer);
|
||||
// Setup a text terminal instance.
|
||||
TextTerminal textterm(&textbufhandle);
|
||||
|
||||
// Register the text terminal as the kernel log and initialize it.
|
||||
Log::Init(PrintToTextTerminal, &textterm);
|
||||
|
||||
// Display the boot welcome screen.
|
||||
DoWelcome();
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "keyboard.h"
|
||||
#include "uart.h"
|
||||
#include "serialterminal.h"
|
||||
#include "vgaterminal.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
using namespace Maxsi;
|
||||
|
@ -144,7 +143,10 @@ namespace Sortix
|
|||
|
||||
size_t Print(void* /*user*/, const char* string, size_t stringlen)
|
||||
{
|
||||
#warning Echoing to the VGA terminal is broken
|
||||
#if 0
|
||||
if ( ECHO_TO_VGA ) { VGATerminal::Print(NULL, string, stringlen); }
|
||||
#endif
|
||||
if ( cursordisabled )
|
||||
{
|
||||
const char* msg = "\e[h";
|
||||
|
|
83
sortix/textbuffer.cpp
Normal file
83
sortix/textbuffer.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
textbuffer.cpp
|
||||
Provides a indexable text buffer for used by text mode terminals.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
TextBufferHandle::TextBufferHandle(TextBuffer* textbuf, bool deletebuf,
|
||||
TextBuffer* def, bool deletedef)
|
||||
{
|
||||
this->textbuf = textbuf;
|
||||
this->deletebuf = deletebuf;
|
||||
this->def = def;
|
||||
this->deletedef = deletedef;
|
||||
this->numused = 0;
|
||||
this->mutex = KTHREAD_MUTEX_INITIALIZER;
|
||||
this->unusedcond = KTHREAD_COND_INITIALIZER;
|
||||
}
|
||||
|
||||
TextBufferHandle::~TextBufferHandle()
|
||||
{
|
||||
if ( deletebuf )
|
||||
delete textbuf;
|
||||
if ( deletedef )
|
||||
delete def;
|
||||
}
|
||||
|
||||
TextBuffer* TextBufferHandle::Acquire()
|
||||
{
|
||||
ScopedLock lock(&mutex);
|
||||
numused++;
|
||||
if ( textbuf )
|
||||
return textbuf;
|
||||
if ( !def )
|
||||
numused--;
|
||||
return def;
|
||||
}
|
||||
|
||||
void TextBufferHandle::Release(TextBuffer* textbuf)
|
||||
{
|
||||
ASSERT(textbuf);
|
||||
ScopedLock lock(&mutex);
|
||||
ASSERT(numused);
|
||||
if ( !--numused )
|
||||
kthread_cond_signal(&unusedcond);
|
||||
}
|
||||
|
||||
void TextBufferHandle::Replace(TextBuffer* newtextbuf, bool deletebuf)
|
||||
{
|
||||
ScopedLock lock(&mutex);
|
||||
while ( numused )
|
||||
kthread_cond_wait(&unusedcond, &mutex);
|
||||
if ( deletebuf )
|
||||
delete textbuf;
|
||||
this->textbuf = newtextbuf;
|
||||
this->deletebuf = deletebuf;
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
417
sortix/textterminal.cpp
Normal file
417
sortix/textterminal.cpp
Normal file
|
@ -0,0 +1,417 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
textterminal.cpp
|
||||
Translates a character stream to a 2 dimensional array of character.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <sortix/vga.h>
|
||||
#include "textterminal.h"
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
const uint16_t DEFAULT_COLOR = COLOR8_LIGHT_GREY << 0U | COLOR8_BLACK << 4U;
|
||||
const uint16_t ATTR_CHAR = 1U << 0U;
|
||||
|
||||
TextTerminal::TextTerminal(TextBufferHandle* textbufhandle)
|
||||
{
|
||||
this->textbufhandle = textbufhandle; textbufhandle->Refer();
|
||||
Reset();
|
||||
}
|
||||
|
||||
void TextTerminal::Reset()
|
||||
{
|
||||
vgacolor = DEFAULT_COLOR;
|
||||
column = line = 0;
|
||||
ansisavedposx = ansisavedposy = 0;
|
||||
ansimode = NONE;
|
||||
TextBuffer* textbuf = textbufhandle->Acquire();
|
||||
TextPos fillfrom(0, 0);
|
||||
TextPos fillto(textbuf->Width()-1, textbuf->Height()-1);
|
||||
TextChar fillwith(' ', vgacolor);
|
||||
textbuf->Fill(fillfrom, fillto, fillwith, 0);
|
||||
textbuf->SetCursorEnabled(true);
|
||||
UpdateCursor(textbuf);
|
||||
textbufhandle->Release(textbuf);
|
||||
}
|
||||
|
||||
size_t TextTerminal::Print(const char* string, size_t stringlen)
|
||||
{
|
||||
TextBuffer* textbuf = textbufhandle->Acquire();
|
||||
for ( size_t i = 0; i < stringlen; i++ )
|
||||
PutChar(textbuf, string[i]);
|
||||
UpdateCursor(textbuf);
|
||||
textbufhandle->Release(textbuf);
|
||||
return stringlen;
|
||||
}
|
||||
|
||||
void TextTerminal::PutChar(TextBuffer* textbuf, char c)
|
||||
{
|
||||
if ( ansimode )
|
||||
PutAnsiEscaped(textbuf, c);
|
||||
else switch ( c )
|
||||
{
|
||||
case '\n': Newline(textbuf); break;
|
||||
case '\r': column = 0; break;
|
||||
case '\b': Backspace(textbuf); break;
|
||||
case '\t': Tab(textbuf); break;
|
||||
case '\e': AnsiReset(); break;
|
||||
default:
|
||||
{
|
||||
if ( textbuf->Width() <= column )
|
||||
Newline(textbuf);
|
||||
TextPos pos(column++, line);
|
||||
TextChar tc(c, vgacolor);
|
||||
textbuf->SetChar(pos, tc);
|
||||
textbuf->SetCharAttr(pos, ATTR_CHAR);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void TextTerminal::UpdateCursor(TextBuffer* textbuf)
|
||||
{
|
||||
textbuf->SetCursorPos(TextPos(column, line));
|
||||
}
|
||||
|
||||
void TextTerminal::Newline(TextBuffer* textbuf)
|
||||
{
|
||||
textbuf->SetCharAttr(TextPos(column, line), ATTR_CHAR);
|
||||
column = 0;
|
||||
if ( line < textbuf->Height()-1 )
|
||||
line++;
|
||||
else
|
||||
textbuf->Scroll(1, TextChar(' ', vgacolor)),
|
||||
line = textbuf->Height()-1;
|
||||
}
|
||||
|
||||
static TextPos DecrementTextPos(TextBuffer* textbuf, TextPos pos)
|
||||
{
|
||||
if ( !pos.x && !pos.y )
|
||||
return pos;
|
||||
if ( !pos.x )
|
||||
return TextPos(textbuf->Width(), pos.y-1);
|
||||
return TextPos(pos.x-1, pos.y);
|
||||
}
|
||||
|
||||
void TextTerminal::Backspace(TextBuffer* textbuf)
|
||||
{
|
||||
TextPos pos(column, line);
|
||||
while ( pos.x || pos.y )
|
||||
{
|
||||
pos = DecrementTextPos(textbuf, pos);
|
||||
uint16_t attr = textbuf->GetCharAttr(pos);
|
||||
textbuf->SetChar(pos, TextChar(' ', vgacolor));
|
||||
textbuf->SetCharAttr(pos, attr & ~ATTR_CHAR);
|
||||
if ( attr & ATTR_CHAR )
|
||||
break;
|
||||
}
|
||||
column = pos.x;
|
||||
line = pos.y;
|
||||
}
|
||||
|
||||
void TextTerminal::Tab(TextBuffer* textbuf)
|
||||
{
|
||||
if ( column == textbuf->Width() )
|
||||
Newline(textbuf);
|
||||
// TODO: This does not work correctly if the text buffer width is not a
|
||||
// multiple of four and the column is near the edge.
|
||||
unsigned until = 4 - (column % 4);
|
||||
textbuf->SetCharAttr(TextPos(column, line), ATTR_CHAR);
|
||||
while ( (until--) != 0 )
|
||||
textbuf->SetChar(TextPos(column++, line), TextChar(' ', vgacolor));
|
||||
}
|
||||
|
||||
// TODO: This implementation of the 'Ansi Escape Codes' is incomplete and hacky.
|
||||
void TextTerminal::AnsiReset()
|
||||
{
|
||||
ansiusedparams = 0;
|
||||
currentparamindex = 0;
|
||||
ansiparams[0] = 0;
|
||||
paramundefined = true;
|
||||
ignoresequence = false;
|
||||
ansimode = CSI;
|
||||
}
|
||||
|
||||
void TextTerminal::PutAnsiEscaped(TextBuffer* textbuf, char c)
|
||||
{
|
||||
// Check the proper prefixes are used.
|
||||
if ( ansimode == CSI )
|
||||
{
|
||||
if ( c != '[' ) { ansimode = NONE; return; }
|
||||
ansimode = COMMAND;
|
||||
return;
|
||||
}
|
||||
|
||||
// Read part of a parameter.
|
||||
if ( '0' <= c && c <= '9' )
|
||||
{
|
||||
if ( paramundefined )
|
||||
ansiusedparams++;
|
||||
paramundefined = false;
|
||||
unsigned val = c - '0';
|
||||
ansiparams[currentparamindex] *= 10;
|
||||
ansiparams[currentparamindex] += val;
|
||||
}
|
||||
|
||||
// Parameter delimiter.
|
||||
else if ( c == ';' )
|
||||
{
|
||||
if ( currentparamindex == ANSI_NUM_PARAMS - 1 )
|
||||
{
|
||||
ansimode = NONE;
|
||||
return;
|
||||
}
|
||||
paramundefined = true;
|
||||
ansiparams[++currentparamindex] = 0;
|
||||
}
|
||||
|
||||
// Left for future standardization, so discard this sequence.
|
||||
else if ( c == ':' )
|
||||
{
|
||||
ignoresequence = true;
|
||||
}
|
||||
|
||||
// Run a command.
|
||||
else if ( 64 <= c && c <= 126 )
|
||||
{
|
||||
if ( !ignoresequence )
|
||||
RunAnsiCommand(textbuf, c);
|
||||
}
|
||||
|
||||
// Something I don't understand, and ignore intentionally.
|
||||
else if ( c == '?' )
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: There are some rare things that should be supported here.
|
||||
|
||||
// Ignore unknown input.
|
||||
else
|
||||
{
|
||||
ansimode = NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void TextTerminal::RunAnsiCommand(TextBuffer* textbuf, char c)
|
||||
{
|
||||
const unsigned width = (unsigned) textbuf->Width();
|
||||
const unsigned height = (unsigned) textbuf->Height();
|
||||
|
||||
switch ( c )
|
||||
{
|
||||
case 'A': // Cursor up
|
||||
{
|
||||
unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( line < dist )
|
||||
line = 0;
|
||||
else
|
||||
line -= dist;
|
||||
} break;
|
||||
case 'B': // Cursor down
|
||||
{
|
||||
unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( height <= line + dist )
|
||||
line = height-1;
|
||||
else
|
||||
line += dist;
|
||||
} break;
|
||||
case 'C': // Cursor forward
|
||||
{
|
||||
unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( width <= column + dist )
|
||||
column = width-1;
|
||||
else
|
||||
column += dist;
|
||||
} break;
|
||||
case 'D': // Cursor backward
|
||||
{
|
||||
unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( column < dist )
|
||||
column = 0;
|
||||
else
|
||||
column -= dist;
|
||||
} break;
|
||||
case 'E': // Move to beginning of line N lines down.
|
||||
{
|
||||
column = 0;
|
||||
unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( height <= line + dist )
|
||||
line = height-1;
|
||||
else
|
||||
line += dist;
|
||||
} break;
|
||||
case 'F': // Move to beginning of line N lines up.
|
||||
{
|
||||
column = 0;
|
||||
unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( line < dist )
|
||||
line = 0;
|
||||
else
|
||||
line -= dist;
|
||||
} break;
|
||||
case 'G': // Move the cursor to column N.
|
||||
{
|
||||
unsigned pos = 0 < ansiusedparams ? ansiparams[0]-1 : 0;
|
||||
if ( width <= pos )
|
||||
pos = width-1;
|
||||
column = pos;
|
||||
} break;
|
||||
case 'H': // Move the cursor to line Y, column X.
|
||||
case 'f':
|
||||
{
|
||||
unsigned posy = 0 < ansiusedparams ? ansiparams[0]-1 : 0;
|
||||
unsigned posx = 1 < ansiusedparams ? ansiparams[1]-1 : 0;
|
||||
if ( width <= posx )
|
||||
posx = width-1;
|
||||
if ( height <= posy )
|
||||
posy = height-1;
|
||||
column = posx;
|
||||
line = posy;
|
||||
} break;
|
||||
case 'J': // Erase parts of the screen.
|
||||
{
|
||||
unsigned mode = 0 < ansiusedparams ? ansiparams[0] : 0;
|
||||
TextPos from(0, 0);
|
||||
TextPos to(0, 0);
|
||||
|
||||
if ( mode == 0 ) // From cursor to end.
|
||||
from = TextPos{column, line},
|
||||
to = TextPos{width-1, height-1};
|
||||
|
||||
if ( mode == 1 ) // From start to cursor.
|
||||
from = TextPos{0, 0},
|
||||
to = TextPos{column, line};
|
||||
|
||||
if ( mode == 2 ) // Everything.
|
||||
from = TextPos{0, 0},
|
||||
to = TextPos{width-1, height-1};
|
||||
|
||||
textbuf->Fill(from, to, TextChar(' ', vgacolor), 0);
|
||||
} break;
|
||||
case 'K': // Erase parts of the current line.
|
||||
{
|
||||
unsigned mode = 0 < ansiusedparams ? ansiparams[0] : 0;
|
||||
TextPos from(0, 0);
|
||||
TextPos to(0, 0);
|
||||
|
||||
if ( mode == 0 ) // From cursor to end.
|
||||
from = TextPos{column, line},
|
||||
to = TextPos{width-1, line};
|
||||
|
||||
if ( mode == 1 ) // From start to cursor.
|
||||
from = TextPos{0, line},
|
||||
to = TextPos{column, line};
|
||||
|
||||
if ( mode == 2 ) // Everything.
|
||||
from = TextPos{0, line},
|
||||
to = TextPos{width-1, line};
|
||||
|
||||
textbuf->Fill(from, to, TextChar(' ', vgacolor), 0);
|
||||
} break;
|
||||
case 'S': // Scroll a line up and place a new line at the buttom.
|
||||
{
|
||||
textbuf->Scroll(1, TextChar(' ', vgacolor));
|
||||
line = height-1;
|
||||
} break;
|
||||
case 'T': // Scroll a line up and place a new line at the top.
|
||||
{
|
||||
textbuf->Scroll(-1, TextChar(' ', vgacolor));
|
||||
line = 0;
|
||||
} break;
|
||||
case 'm': // Change how the text is rendered.
|
||||
{
|
||||
if ( ansiusedparams == 0 )
|
||||
{
|
||||
ansiparams[0] = 0;
|
||||
ansiusedparams++;
|
||||
}
|
||||
|
||||
// Convert from the ANSI color scheme to the VGA color scheme.
|
||||
const unsigned conversion[8] =
|
||||
{
|
||||
COLOR8_BLACK, COLOR8_RED, COLOR8_GREEN, COLOR8_BROWN,
|
||||
COLOR8_BLUE, COLOR8_MAGENTA, COLOR8_CYAN, COLOR8_LIGHT_GREY,
|
||||
};
|
||||
|
||||
for ( size_t i = 0; i < ansiusedparams; i++ )
|
||||
{
|
||||
unsigned cmd = ansiparams[i];
|
||||
// Turn all attributes off.
|
||||
if ( cmd == 0 )
|
||||
{
|
||||
vgacolor = DEFAULT_COLOR;
|
||||
}
|
||||
// Set text color.
|
||||
else if ( 30 <= cmd && cmd <= 37 )
|
||||
{
|
||||
unsigned val = cmd - 30;
|
||||
vgacolor &= 0xF0;
|
||||
vgacolor |= conversion[val] << 0;
|
||||
}
|
||||
// Set background color.
|
||||
else if ( 40 <= cmd && cmd <= 47 )
|
||||
{
|
||||
unsigned val = cmd - 40;
|
||||
vgacolor &= 0x0F;
|
||||
vgacolor |= conversion[val] << 4;
|
||||
}
|
||||
// TODO: There are many other things we don't support.
|
||||
}
|
||||
} break;
|
||||
case 'n': // Request special information from terminal.
|
||||
{
|
||||
// TODO: Handle this code.
|
||||
} break;
|
||||
case 's': // Save cursor position.
|
||||
{
|
||||
ansisavedposx = column;
|
||||
ansisavedposx = line;
|
||||
} break;
|
||||
case 'u': // Restore cursor position.
|
||||
{
|
||||
column = ansisavedposx;
|
||||
line = ansisavedposx;
|
||||
if ( width <= column )
|
||||
column = width-1;
|
||||
if ( height <= line )
|
||||
line = height-1;
|
||||
} break;
|
||||
case 'l': // Hide cursor.
|
||||
{
|
||||
// TODO: This is somehow related to the special char '?'.
|
||||
if ( 0 < ansiusedparams && ansiparams[0] == 25 )
|
||||
textbuf->SetCursorEnabled(false);
|
||||
} break;
|
||||
case 'h': // Show cursor.
|
||||
{
|
||||
// TODO: This is somehow related to the special char '?'.
|
||||
if ( 0 < ansiusedparams && ansiparams[0] == 25 )
|
||||
textbuf->SetCursorEnabled(true);
|
||||
} break;
|
||||
// TODO: Handle other cases.
|
||||
}
|
||||
|
||||
ansimode = NONE;
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
69
sortix/textterminal.h
Normal file
69
sortix/textterminal.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
textterminal.cpp
|
||||
An indexable text buffer with the VGA text mode framebuffer as backend.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_TEXTTERMINAL_H
|
||||
#define SORTIX_TEXTTERMINAL_H
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class TextBufferHandle;
|
||||
|
||||
class TextTerminal //: public Printable ?
|
||||
{
|
||||
public:
|
||||
TextTerminal(TextBufferHandle* textbufhandle);
|
||||
~TextTerminal();
|
||||
size_t Print(const char* string, size_t stringlen);
|
||||
|
||||
private:
|
||||
void PutChar(TextBuffer* textbuf, char c);
|
||||
void UpdateCursor(TextBuffer* textbuf);
|
||||
void Newline(TextBuffer* textbuf);
|
||||
void Backspace(TextBuffer* textbuf);
|
||||
void Tab(TextBuffer* textbuf);
|
||||
void PutAnsiEscaped(TextBuffer* textbuf, char c);
|
||||
void RunAnsiCommand(TextBuffer* textbuf, char c);
|
||||
void AnsiReset();
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
TextBufferHandle* textbufhandle;
|
||||
uint8_t vgacolor;
|
||||
unsigned column;
|
||||
unsigned line;
|
||||
unsigned ansisavedposx;
|
||||
unsigned ansisavedposy;
|
||||
enum { NONE = 0, CSI, COMMAND, } ansimode;
|
||||
static const size_t ANSI_NUM_PARAMS = 16;
|
||||
unsigned ansiusedparams;
|
||||
unsigned ansiparams[ANSI_NUM_PARAMS];
|
||||
unsigned currentparamindex;
|
||||
bool paramundefined;
|
||||
bool ignoresequence;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
|
@ -1,547 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
vgaterminal.cpp
|
||||
A terminal based on a text mode buffer.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/log.h>
|
||||
#include <sortix/vga.h>
|
||||
#include <libmaxsi/memory.h>
|
||||
#include "vga.h"
|
||||
#include "vgaterminal.h"
|
||||
|
||||
using namespace Maxsi;
|
||||
|
||||
namespace Sortix {
|
||||
namespace VGATerminal {
|
||||
|
||||
const unsigned width = 80;
|
||||
const unsigned height = 25;
|
||||
const uint16_t DEFAULT_COLOR = (COLOR8_LIGHT_GREY << 8) | (COLOR8_BLACK << 12);
|
||||
uint16_t* const vga = (uint16_t* const) 0xB8000;
|
||||
uint16_t vgaattr[width * height];
|
||||
const uint16_t VGAATTR_CHAR = (1U<<0U);
|
||||
unsigned line;
|
||||
unsigned column;
|
||||
uint16_t currentcolor;
|
||||
unsigned ansisavedposx;
|
||||
unsigned ansisavedposy;
|
||||
bool showcursor;
|
||||
|
||||
enum
|
||||
{
|
||||
NONE,
|
||||
CSI,
|
||||
COMMAND,
|
||||
} ansimode;
|
||||
|
||||
void UpdateCursor()
|
||||
{
|
||||
if ( showcursor )
|
||||
VGA::SetCursor(column, line);
|
||||
else
|
||||
VGA::SetCursor(width, height-1);
|
||||
}
|
||||
|
||||
// Clear the screen, put the cursor at the top left corner, set default
|
||||
// text color, and reset ANSI escape sequence state.
|
||||
void Reset()
|
||||
{
|
||||
ansimode = NONE;
|
||||
|
||||
line = 0;
|
||||
column = 0;
|
||||
ansisavedposx = 0;
|
||||
ansisavedposy = 0;
|
||||
|
||||
currentcolor = DEFAULT_COLOR;
|
||||
|
||||
for ( nat y = 0; y < height; y++ )
|
||||
{
|
||||
for ( nat x = 0; x < width; x++ )
|
||||
{
|
||||
unsigned index = y * width + x;
|
||||
vga[index] = ' ' | DEFAULT_COLOR;
|
||||
vgaattr[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the VGA cursor.
|
||||
showcursor = true;
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
// Initialize the terminal driver.
|
||||
void Init()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
// Move every line one row up and leaves an empty line at the bottom.
|
||||
void ScrollUp()
|
||||
{
|
||||
for ( nat y = 1; y < height; y++ )
|
||||
{
|
||||
size_t linesize = width * sizeof(uint16_t);
|
||||
size_t fromoff = (y-0) * width;
|
||||
size_t destoff = (y-1) * width;
|
||||
Memory::Copy(vga + destoff, vga + fromoff, linesize);
|
||||
Memory::Copy(vgaattr + destoff, vgaattr + fromoff, linesize);
|
||||
}
|
||||
|
||||
for ( nat x = 0; x < width; x++ )
|
||||
{
|
||||
unsigned index = (height-1) * width + x;
|
||||
vga[index] = ' ' | currentcolor;
|
||||
vgaattr[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Move every line one row down and leaves an empty line at the top.
|
||||
void ScrollDown()
|
||||
{
|
||||
for ( nat y = 1; y < height; y++ )
|
||||
{
|
||||
size_t linesize = width * sizeof(uint16_t);
|
||||
size_t fromoff = (y-1) * width;
|
||||
size_t destoff = (y-0) * width;
|
||||
Memory::Copy(vga + destoff, vga + fromoff, linesize);
|
||||
Memory::Copy(vgaattr + destoff, vgaattr + fromoff, linesize);
|
||||
}
|
||||
|
||||
for ( nat x = 0; x < width; x++ )
|
||||
{
|
||||
unsigned index = x;
|
||||
vga[index] = ' ' | currentcolor;
|
||||
vgaattr[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next line. If at bottom, scroll one line up.
|
||||
void Newline()
|
||||
{
|
||||
vgaattr[line * width + column] |= VGAATTR_CHAR;
|
||||
if ( line < height - 1 )
|
||||
line++;
|
||||
else
|
||||
ScrollUp();
|
||||
column = 0;
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
void ANSIReset();
|
||||
void ParseANSIEscape(char c);
|
||||
|
||||
// Print text to the vga framebuffer, simulating how a terminal would
|
||||
// display the text.
|
||||
size_t Print(void* /*user*/, const char* string, size_t stringlen)
|
||||
{
|
||||
// Iterate over each character.
|
||||
size_t left = stringlen;
|
||||
while ( (left--) > 0 )
|
||||
{
|
||||
char c = *(string++);
|
||||
|
||||
// If we are parsing an escape sequence, parse another char.
|
||||
if ( ansimode != NONE ) { ParseANSIEscape(c); continue; }
|
||||
|
||||
switch ( c )
|
||||
{
|
||||
// Move cursor to the next line and scroll up (if needed).
|
||||
case '\n':
|
||||
{
|
||||
Newline();
|
||||
break;
|
||||
}
|
||||
// Move cursor to start of line.
|
||||
case '\r':
|
||||
{
|
||||
column = 0;
|
||||
break;
|
||||
}
|
||||
// Delete the previous character.
|
||||
case '\b':
|
||||
{
|
||||
unsigned pos = line * width + column;
|
||||
while ( pos )
|
||||
{
|
||||
unsigned nextpos = pos-1;
|
||||
vga[nextpos] = ' ' | currentcolor;
|
||||
pos = nextpos;
|
||||
uint16_t attr = vgaattr[pos];
|
||||
vgaattr[pos] = 0;
|
||||
if ( attr & VGAATTR_CHAR ) { break; }
|
||||
}
|
||||
column = pos % width;
|
||||
line = pos / width;
|
||||
break;
|
||||
}
|
||||
// Expand a tab to a few spaces.
|
||||
case '\t':
|
||||
{
|
||||
if ( column == width ) { Newline(); }
|
||||
nat until = 4 - (column % 4);
|
||||
|
||||
vgaattr[line * width + (column)] |= VGAATTR_CHAR;
|
||||
while ( (until--) != 0 )
|
||||
{
|
||||
vga[line * width + (column++)] = ' ' | currentcolor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Initialize an ANSI escape sequence, allowing much more
|
||||
// powerful control of the terminal than ASCII does.
|
||||
case '\e':
|
||||
{
|
||||
ANSIReset();
|
||||
break;
|
||||
}
|
||||
// Print this character at the cursor and increment the cursor.
|
||||
default:
|
||||
{
|
||||
if ( column == width ) { Newline(); }
|
||||
unsigned index = line * width + (column++);
|
||||
vga[index] = c | currentcolor;
|
||||
vgaattr[index] |= VGAATTR_CHAR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the VGA hardware cursor.
|
||||
UpdateCursor();
|
||||
|
||||
return stringlen;
|
||||
}
|
||||
|
||||
const size_t ANSI_NUM_PARAMS = 16;
|
||||
nat ansiusedparams;
|
||||
nat ansiparams[ANSI_NUM_PARAMS];
|
||||
nat currentparamindex;
|
||||
bool paramundefined;
|
||||
bool ignoresequence;
|
||||
|
||||
// TODO: The ANSI escape code is only partially implemented!
|
||||
|
||||
// Begin an ANSI esacpe sequence.
|
||||
void ANSIReset()
|
||||
{
|
||||
if ( ansimode == NONE )
|
||||
{
|
||||
ansiusedparams = 0;
|
||||
currentparamindex = 0;
|
||||
ansiparams[0] = 0;
|
||||
paramundefined = true;
|
||||
ignoresequence = false;
|
||||
ansimode = CSI;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads parameters and changes font properties accordingly.
|
||||
void AnsiSetFont()
|
||||
{
|
||||
// Convert from the ANSI color scheme to the VGA color scheme.
|
||||
const unsigned conversion[8] =
|
||||
{
|
||||
COLOR8_BLACK, COLOR8_RED, COLOR8_GREEN, COLOR8_BROWN,
|
||||
COLOR8_BLUE, COLOR8_MAGENTA, COLOR8_CYAN, COLOR8_LIGHT_GREY,
|
||||
};
|
||||
|
||||
for ( size_t i = 0; i < ansiusedparams; i++ )
|
||||
{
|
||||
nat cmd = ansiparams[i];
|
||||
// Turn all attributes off.
|
||||
if ( cmd == 0 )
|
||||
{
|
||||
currentcolor = DEFAULT_COLOR;
|
||||
}
|
||||
// Set text color.
|
||||
else if ( 30 <= cmd && cmd <= 37 )
|
||||
{
|
||||
nat val = cmd - 30;
|
||||
currentcolor &= (0xF0FF);
|
||||
currentcolor |= (conversion[val] << 8);
|
||||
}
|
||||
// Set background color.
|
||||
else if ( 40 <= cmd && cmd <= 47 )
|
||||
{
|
||||
nat val = cmd - 40;
|
||||
currentcolor &= (0x0FFF);
|
||||
currentcolor |= (conversion[val] << 12);
|
||||
}
|
||||
// TODO: There are many other things we don't support.
|
||||
}
|
||||
}
|
||||
|
||||
// Executes an ASNI escape command based on given parameters.
|
||||
void RunANSICommand(char c)
|
||||
{
|
||||
switch ( c )
|
||||
{
|
||||
// Cursor up
|
||||
case 'A':
|
||||
{
|
||||
nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1;
|
||||
if ( line < dist ) { line = 0; } else { line -= dist; }
|
||||
break;
|
||||
}
|
||||
// Cursor down
|
||||
case 'B':
|
||||
{
|
||||
nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1;
|
||||
if ( height <= line + dist ) { line = height-1; } else { line += dist; }
|
||||
break;
|
||||
}
|
||||
// Cursor forward
|
||||
case 'C':
|
||||
{
|
||||
nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1;
|
||||
if ( width <= column + dist ) { column = width-1; } else { column += dist; }
|
||||
break;
|
||||
}
|
||||
// Cursor backward
|
||||
case 'D':
|
||||
{
|
||||
nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1;
|
||||
if ( column < dist ) { column = 0; } else { column -= dist; }
|
||||
break;
|
||||
}
|
||||
// Move to beginning of line N lines down.
|
||||
case 'E':
|
||||
{
|
||||
column = 0;
|
||||
nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1;
|
||||
if ( height <= line + dist ) { line = height-1; } else { line += dist; }
|
||||
break;
|
||||
}
|
||||
// Move to beginning of line N lines up.
|
||||
case 'F':
|
||||
{
|
||||
column = 0;
|
||||
nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1;
|
||||
if ( line < dist ) { line = 0; } else { line -= dist; }
|
||||
break;
|
||||
}
|
||||
// Move the cursor to column N.
|
||||
case 'G':
|
||||
{
|
||||
nat pos = ( 0 < ansiusedparams ) ? ansiparams[0]-1 : 0;
|
||||
if ( width <= pos ) { pos = width-1; }
|
||||
column = pos;
|
||||
break;
|
||||
}
|
||||
// Move the cursor to line Y, column X.
|
||||
case 'H':
|
||||
case 'f':
|
||||
{
|
||||
nat posy = ( 0 < ansiusedparams ) ? ansiparams[0]-1 : 0;
|
||||
nat posx = ( 1 < ansiusedparams ) ? ansiparams[1]-1 : 0;
|
||||
if ( width <= posx ) { posx = width-1; }
|
||||
if ( height <= posy ) { posy = height-1; }
|
||||
column = posx;
|
||||
line = posy;
|
||||
break;
|
||||
}
|
||||
// Erase parts of the screen.
|
||||
case 'J':
|
||||
{
|
||||
nat mode = ( 0 < ansiusedparams ) ? ansiparams[0] : 0;
|
||||
|
||||
nat from = 0;
|
||||
nat to = 0;
|
||||
|
||||
// From cursor to end.
|
||||
if ( mode == 0 ) { from = line*width + column; to = height*width - 1; }
|
||||
|
||||
// From start to cursor.
|
||||
if ( mode == 1 ) { from = 0; to = line*width + column; }
|
||||
|
||||
// Everything.
|
||||
if ( mode == 2 ) { from = 0; to = height*width - 1; }
|
||||
|
||||
for ( nat i = from; i <= to; i++ )
|
||||
{
|
||||
vga[i] = ' ' | currentcolor;
|
||||
vgaattr[i] = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// Erase parts of the current line.
|
||||
case 'K':
|
||||
{
|
||||
nat mode = ( 0 < ansiusedparams ) ? ansiparams[0] : 0;
|
||||
|
||||
nat from = 0;
|
||||
nat to = 0;
|
||||
|
||||
// From cursor to end.
|
||||
if ( mode == 0 ) { from = column; to = width - 1; }
|
||||
|
||||
// From start to cursor.
|
||||
if ( mode == 1 ) { from = 0; to = column; }
|
||||
|
||||
// Everything.
|
||||
if ( mode == 2 ) { from = 0; to = width - 1; }
|
||||
|
||||
for ( nat i = from; i <= to; i++ )
|
||||
{
|
||||
unsigned index = line * width + i;
|
||||
vga[index] = ' ' | currentcolor;
|
||||
vgaattr[index] = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// Scroll a line up and place a new line at the buttom.
|
||||
case 'S':
|
||||
{
|
||||
ScrollUp(); line = height-1;
|
||||
break;
|
||||
}
|
||||
// Scroll a line up and place a new line at the top.
|
||||
case 'T':
|
||||
{
|
||||
ScrollDown(); line = 0;
|
||||
break;
|
||||
}
|
||||
// Change how the text is rendered.
|
||||
case 'm':
|
||||
{
|
||||
if ( ansiusedparams == 0 )
|
||||
{
|
||||
ansiparams[0] = 0;
|
||||
ansiusedparams++;
|
||||
}
|
||||
AnsiSetFont();
|
||||
break;
|
||||
}
|
||||
// Request special information from terminal.
|
||||
case 'n':
|
||||
{
|
||||
// TODO: Handle this code.
|
||||
break;
|
||||
}
|
||||
// Save cursor position.
|
||||
case 's':
|
||||
{
|
||||
ansisavedposx = column;
|
||||
ansisavedposx = line;
|
||||
break;
|
||||
}
|
||||
// Restore cursor position.
|
||||
case 'u':
|
||||
{
|
||||
column = ansisavedposx;
|
||||
line = ansisavedposx;
|
||||
if ( width <= column ) { column = width-1; }
|
||||
if ( height <= line ) { line = height-1; }
|
||||
break;
|
||||
}
|
||||
// Hide cursor.
|
||||
case 'l':
|
||||
{
|
||||
// TODO: This is somehow related to the special char '?'.
|
||||
if ( 0 < ansiusedparams && ansiparams[0] == 25 )
|
||||
{
|
||||
showcursor = false;
|
||||
UpdateCursor();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Show cursor.
|
||||
case 'h':
|
||||
{
|
||||
// TODO: This is somehow related to the special char '?'.
|
||||
if ( 0 < ansiusedparams && ansiparams[0] == 25 )
|
||||
{
|
||||
showcursor = true;
|
||||
UpdateCursor();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// TODO: Handle other cases.
|
||||
}
|
||||
|
||||
ansimode = NONE;
|
||||
}
|
||||
|
||||
// Parse another char of an ASNI escape code.
|
||||
void ParseANSIEscape(char c)
|
||||
{
|
||||
// Check the proper prefixes are used.
|
||||
if ( ansimode == CSI )
|
||||
{
|
||||
if ( c != '[' ) { ansimode = NONE; return; }
|
||||
ansimode = COMMAND;
|
||||
}
|
||||
|
||||
// Parse parameters and the command.
|
||||
else if ( ansimode == COMMAND )
|
||||
{
|
||||
// Read part of a parameter.
|
||||
if ( '0' <= c && c <= '9' )
|
||||
{
|
||||
if ( paramundefined ) { ansiusedparams++; }
|
||||
paramundefined = false;
|
||||
nat val = c - '0';
|
||||
ansiparams[currentparamindex] *= 10;
|
||||
ansiparams[currentparamindex] += val;
|
||||
}
|
||||
|
||||
// Parameter delimiter.
|
||||
else if ( c == ';' )
|
||||
{
|
||||
if ( currentparamindex == ANSI_NUM_PARAMS - 1 ) { ansimode = NONE; return; }
|
||||
paramundefined = true;
|
||||
ansiparams[++currentparamindex] = 0;
|
||||
}
|
||||
|
||||
// Left for future standardization, so discard this sequence.
|
||||
else if ( c == ':' )
|
||||
{
|
||||
ignoresequence = true;
|
||||
}
|
||||
|
||||
// Run a command.
|
||||
else if ( 64 <= c && c <= 126 )
|
||||
{
|
||||
if ( !ignoresequence ) { RunANSICommand(c); }
|
||||
}
|
||||
|
||||
// Something I don't understand, and ignore intentionally.
|
||||
else if ( c == '?' )
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: There are some rare things that should be supported here.
|
||||
|
||||
// Ignore unknown input.
|
||||
else
|
||||
{
|
||||
ansimode = NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VGATerminal
|
||||
} // namespace Sortix
|
|
@ -1,39 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
vgaterminal.cpp
|
||||
A terminal based on a text mode buffer.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_VGATERMINAL_H
|
||||
#define SORTIX_VGATERMINAL_H
|
||||
|
||||
namespace Sortix {
|
||||
namespace VGATerminal {
|
||||
|
||||
void Init();
|
||||
void Reset();
|
||||
size_t Print(void* user, const char* string, size_t stringlen);
|
||||
|
||||
} // namespace VGATerminal
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
||||
|
193
sortix/vgatextbuffer.cpp
Normal file
193
sortix/vgatextbuffer.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
vgatextbuffer.cpp
|
||||
An indexable text buffer with the VGA text mode framebuffer as backend.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sortix/kernel/platform.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
#include <sortix/vga.h>
|
||||
#include "vga.h"
|
||||
#include "vgatextbuffer.h"
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
VGATextBuffer::VGATextBuffer(uint16_t* vga, uint16_t* attr,
|
||||
size_t width, size_t height)
|
||||
{
|
||||
this->vga = vga;
|
||||
this->attr = attr;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
cursorpos = {0, 0};
|
||||
cursorenabled = true;
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
VGATextBuffer::~VGATextBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
static TextChar EntryToTextChar(uint16_t entry)
|
||||
{
|
||||
char c = entry & 0x00FF;
|
||||
uint8_t vgacolor = entry >> 8U;
|
||||
return TextChar{c, vgacolor};
|
||||
}
|
||||
|
||||
static uint16_t CharToTextEntry(TextChar c)
|
||||
{
|
||||
return (uint16_t) c.c | (uint16_t) c.vgacolor << 8U;
|
||||
}
|
||||
|
||||
|
||||
bool VGATextBuffer::UsablePosition(TextPos pos) const
|
||||
{
|
||||
return pos.x < width && pos.y < height;
|
||||
}
|
||||
|
||||
TextPos VGATextBuffer::CropPosition(TextPos pos) const
|
||||
{
|
||||
if ( width <= pos.x )
|
||||
pos.x = width - 1;
|
||||
if ( height <= pos.y )
|
||||
pos.y = height -1;
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t VGATextBuffer::OffsetOfPos(TextPos pos) const
|
||||
{
|
||||
return pos.y * width + pos.x;
|
||||
}
|
||||
|
||||
size_t VGATextBuffer::Width() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
size_t VGATextBuffer::Height() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
TextChar VGATextBuffer::GetChar(TextPos pos) const
|
||||
{
|
||||
if ( UsablePosition(pos) )
|
||||
return EntryToTextChar(vga[OffsetOfPos(pos)]);
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
void VGATextBuffer::SetChar(TextPos pos, TextChar c)
|
||||
{
|
||||
if ( UsablePosition(pos) )
|
||||
vga[OffsetOfPos(pos)] = CharToTextEntry(c);
|
||||
}
|
||||
|
||||
uint16_t VGATextBuffer::GetCharAttr(TextPos pos) const
|
||||
{
|
||||
if ( UsablePosition(pos) )
|
||||
return attr[OffsetOfPos(pos)];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VGATextBuffer::SetCharAttr(TextPos pos, uint16_t attrval)
|
||||
{
|
||||
if ( UsablePosition(pos) )
|
||||
attr[OffsetOfPos(pos)] = attrval;
|
||||
}
|
||||
|
||||
void VGATextBuffer::Scroll(ssize_t off, TextChar fillwith)
|
||||
{
|
||||
if ( !off )
|
||||
return;
|
||||
bool neg = 0 < off;
|
||||
size_t absoff = off < 0 ? -off : off;
|
||||
if ( height < absoff )
|
||||
absoff = height;
|
||||
TextPos scrollfrom = neg ? TextPos{0, absoff} : TextPos{0, 0};
|
||||
TextPos scrollto = neg ? TextPos{0, 0} : TextPos{0, absoff};
|
||||
TextPos fillfrom = neg ? TextPos{0, height-absoff} : TextPos{0, 0};
|
||||
TextPos fillto = neg ? TextPos{width-1, height-1} : TextPos{width-1, absoff-1};
|
||||
size_t scrollchars = width * (height-absoff);
|
||||
Move(scrollto, scrollfrom, scrollchars);
|
||||
Fill(fillfrom, fillto, fillwith, 0);
|
||||
}
|
||||
|
||||
void VGATextBuffer::Move(TextPos to, TextPos from, size_t numchars)
|
||||
{
|
||||
size_t dest = OffsetOfPos(CropPosition(to));
|
||||
size_t src = OffsetOfPos(CropPosition(from));
|
||||
if ( dest < src )
|
||||
for ( size_t i = 0; i < numchars; i++ )
|
||||
vga[dest + i] = vga[src + i],
|
||||
attr[dest + i] = attr[src + i];
|
||||
else if ( src < dest )
|
||||
for ( size_t i = 0; i < numchars; i++ )
|
||||
vga[dest + numchars-1 - i] = vga[src + numchars-1 - i],
|
||||
attr[dest + numchars-1 - i] = attr[src + numchars-1 - i];
|
||||
}
|
||||
|
||||
void VGATextBuffer::Fill(TextPos from, TextPos to, TextChar fillwith,
|
||||
uint16_t fillattr)
|
||||
{
|
||||
from = CropPosition(from);
|
||||
to = CropPosition(to);
|
||||
size_t start = OffsetOfPos(from);
|
||||
size_t end = OffsetOfPos(to);
|
||||
size_t entry = CharToTextEntry(fillwith);
|
||||
for ( size_t i = start; i <= end; i++ )
|
||||
vga[i] = entry,
|
||||
attr[i] = fillattr;
|
||||
}
|
||||
|
||||
bool VGATextBuffer::GetCursorEnabled() const
|
||||
{
|
||||
return cursorenabled;
|
||||
}
|
||||
|
||||
void VGATextBuffer::SetCursorEnabled(bool enablecursor)
|
||||
{
|
||||
cursorenabled = enablecursor;
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
TextPos VGATextBuffer::GetCursorPos() const
|
||||
{
|
||||
return cursorpos;
|
||||
}
|
||||
|
||||
void VGATextBuffer::SetCursorPos(TextPos cursorpos)
|
||||
{
|
||||
this->cursorpos = cursorpos;
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
void VGATextBuffer::UpdateCursor()
|
||||
{
|
||||
if ( cursorenabled )
|
||||
VGA::SetCursor(cursorpos.x, cursorpos.y);
|
||||
else
|
||||
VGA::SetCursor(width, height-1);
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
70
sortix/vgatextbuffer.h
Normal file
70
sortix/vgatextbuffer.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2012.
|
||||
|
||||
This file is part of Sortix.
|
||||
|
||||
Sortix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
vgatextbuffer.h
|
||||
An indexable text buffer with the VGA text mode framebuffer as backend.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef SORTIX_VGATEXTBUFFER_H
|
||||
#define SORTIX_VGATEXTBUFFER_H
|
||||
|
||||
#include <sortix/kernel/textbuffer.h>
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class VGATextBuffer : public TextBuffer
|
||||
{
|
||||
public:
|
||||
VGATextBuffer(uint16_t* vga, uint16_t* attr, size_t width, size_t height);
|
||||
virtual ~VGATextBuffer();
|
||||
virtual size_t Width() const;
|
||||
virtual size_t Height() const;
|
||||
virtual TextChar GetChar(TextPos pos) const;
|
||||
virtual void SetChar(TextPos pos, TextChar c);
|
||||
virtual uint16_t GetCharAttr(TextPos pos) const ;
|
||||
virtual void SetCharAttr(TextPos pos, uint16_t attrval);
|
||||
virtual void Scroll(ssize_t off, TextChar fillwith);
|
||||
virtual void Move(TextPos to, TextPos from, size_t numchars);
|
||||
virtual void Fill(TextPos from, TextPos to, TextChar fillwith,
|
||||
uint16_t fillattr);
|
||||
virtual bool GetCursorEnabled() const;
|
||||
virtual void SetCursorEnabled(bool enablecursor);
|
||||
virtual TextPos GetCursorPos() const;
|
||||
virtual void SetCursorPos(TextPos cursorpos);
|
||||
|
||||
private:
|
||||
bool UsablePosition(TextPos pos) const;
|
||||
TextPos CropPosition(TextPos pos) const;
|
||||
size_t OffsetOfPos(TextPos pos) const;
|
||||
void UpdateCursor();
|
||||
|
||||
private:
|
||||
uint16_t* vga;
|
||||
uint16_t* attr;
|
||||
size_t width;
|
||||
size_t height;
|
||||
TextPos cursorpos;
|
||||
bool cursorenabled;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Sortix
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue