Finish initial ANSI Escape Sequence support - basic CSI sequences (cursor moving, line erasing) work
This commit is contained in:
parent
94c1eb2279
commit
bc6f94e353
234
arch/i386/tty.c
234
arch/i386/tty.c
|
@ -3,8 +3,8 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <kernel/tty.h>
|
||||
#include <kernel/utils.h>
|
||||
#include "kernel/tty.h"
|
||||
#include "kernel/utils.h"
|
||||
|
||||
static const size_t TERM_WIDTH=80;
|
||||
static const size_t TERM_HEIGHT=25;
|
||||
|
@ -51,76 +51,114 @@ void term_putchar(char c) {
|
|||
break;
|
||||
default:
|
||||
term_putentryat(uc, current_color, terminal_column, terminal_row);
|
||||
break;
|
||||
}
|
||||
|
||||
if(++terminal_column == TERM_WIDTH)
|
||||
{
|
||||
if (++terminal_column == TERM_WIDTH) {
|
||||
terminal_column = 0;
|
||||
if(++terminal_row == TERM_HEIGHT){
|
||||
if (++terminal_row == TERM_HEIGHT) {
|
||||
term_scroll(false);
|
||||
terminal_row = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct csi_sequence parse_csi(const char* data, size_t size) {
|
||||
enum State { PARAMETER, INTERMEDIATE, FINAL, INVALID };
|
||||
enum State state = PARAMETER;
|
||||
|
||||
struct csi_sequence sequence = {
|
||||
.parameter = NULL,
|
||||
.parameter_len = 0,
|
||||
.intermediate = NULL,
|
||||
.intermediate_len = 0,
|
||||
.final = NULL,
|
||||
.valid = false
|
||||
};
|
||||
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
uint8_t c = data[j];
|
||||
if (state == PARAMETER && (c >= 0x30 && c <= 0x3F)) {
|
||||
if (!sequence.parameter)
|
||||
sequence.parameter = data + j;
|
||||
sequence.parameter_len++;
|
||||
} else if (c >= 0x20 && c <= 0x2F) {
|
||||
if (!sequence.intermediate)
|
||||
sequence.intermediate = data + j;
|
||||
sequence.intermediate_len++;
|
||||
state = INTERMEDIATE;
|
||||
} else if (c >= 0x40 && c <= 0x7F) {
|
||||
sequence.final = data + j;
|
||||
sequence.valid = true;
|
||||
state = FINAL;
|
||||
break;
|
||||
} else {
|
||||
// Parameter found in intermediate byte location, or byte out of
|
||||
// range
|
||||
state = INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sequence;
|
||||
}
|
||||
|
||||
void term_write(const char* data, size_t size) {
|
||||
for(size_t i = 0; i < size; i++) {
|
||||
//Begin handling ANSI escape codes.
|
||||
if(data[i] == 0x1B) { //The current character is ESC - the start of ANSI codes.
|
||||
term_writes("ANSI Code encountered: ");
|
||||
if(data[i] == 0x1b) { //The current character is ESC - the start of ANSI codes.
|
||||
//term_writes("ANSI Code encountered: ");
|
||||
|
||||
bool string_terminated = false; //Flag used in some of the escape codes
|
||||
switch(data[i+1]) {
|
||||
case 0x9b/*[*/: //CSI - Control Sequence Introducer (The most common one, hence it comes first)
|
||||
char* controlSequence;
|
||||
if(data[i+2] == NULL) {
|
||||
controlSequence = '\0';
|
||||
for(size_t j = 0; j < (strlen(data) - (i+2); j++) {
|
||||
if(data[j] == ' ')
|
||||
break;
|
||||
controlSequence += data[j];
|
||||
}
|
||||
handleControlSequence(controlSequence); //Send it off to our handler function to keep this part clean
|
||||
break;
|
||||
|
||||
// TODO: Should only progress if we have at least 2 more bytes
|
||||
|
||||
switch ((uint8_t)data[i + 1]) {
|
||||
case '[': //CSI - Control Sequence Introducer (The most common one, hence it comes first)
|
||||
{
|
||||
struct csi_sequence sequence = parse_csi(data + i + 2, size - (i + 2));
|
||||
if (sequence.valid) {
|
||||
// Send it off to our handler function to keep this part clean
|
||||
handleControlSequence(sequence);
|
||||
i += sequence.parameter_len + sequence.intermediate_len + 2; // Move past sequence
|
||||
}
|
||||
}
|
||||
break;
|
||||
//Single shifts are not handled, so just print them and exit
|
||||
case 0x8e/*N*/: //SS2 - Single Shift Two
|
||||
case 'N': //SS2 - Single Shift Two
|
||||
term_writes("Single Shift Two\n");
|
||||
break;
|
||||
case 0x8f/*O*/: //SS3 - Single Shift Three
|
||||
case 'O': //SS3 - Single Shift Three
|
||||
term_writes("Single Shift Three\n");
|
||||
break;
|
||||
|
||||
//Control Strings
|
||||
case 0x90/*P*/: //DCS - Device Control String
|
||||
case 'P': //DCS - Device Control String
|
||||
term_writes("Device Control String");
|
||||
string_terminated = false;
|
||||
break;
|
||||
case 0x9c/*\*/: //ST - String Terminator
|
||||
case '\\': //ST - String Terminator
|
||||
term_writes("String Terminator\n");
|
||||
string_terminated = true;
|
||||
break;
|
||||
case 0x9d/*]*/: //OSC - Operating System Command
|
||||
case ']': //OSC - Operating System Command
|
||||
term_writes("Operating System Command\n");
|
||||
string_terminated = false;
|
||||
break;
|
||||
case 0x98/*X*/: //SOS - Start Of String
|
||||
case 'X': //SOS - Start Of String
|
||||
term_writes("Start of String");
|
||||
string_terminated = false;
|
||||
break;
|
||||
case 0x9e/*^*/: //PM - Privacy Message
|
||||
case '^': //PM - Privacy Message
|
||||
term_writes("Privacy Message\n");
|
||||
break;
|
||||
case 0x9f/*_*/: //APC - Application Program Command
|
||||
case '_': //APC - Application Program Command
|
||||
term_writes("Application Program Command\n");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
term_putchar(data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void term_writes(const char* data) {
|
||||
|
@ -132,105 +170,104 @@ void puts(const char* string) {
|
|||
term_putchar('\n');
|
||||
}
|
||||
|
||||
void handleControlSequence(const char* sequence) {
|
||||
void handleControlSequence(struct csi_sequence sequence) {
|
||||
//Check for our failsafes
|
||||
if(sequence) {
|
||||
if(sequence != '\0') {
|
||||
|
||||
if (sequence.valid) {
|
||||
int n = 0; //Default of the flag used for a few items
|
||||
|
||||
bool nPresent = false;
|
||||
bool terminated = false;
|
||||
for(size_t i = 0; i < strlen(sequence); i++) {
|
||||
if(isDigit(sequence[i])) {
|
||||
nPresent = true;
|
||||
n = (n*10) + sequence[i];
|
||||
} else if(n > 80) {
|
||||
n = 80;
|
||||
break;
|
||||
// Parse parameters, we only care about a max 2 of number only parameters
|
||||
// for now
|
||||
int params[2] = {0};
|
||||
int param_count = 0;
|
||||
if (sequence.parameter_len) {
|
||||
for (size_t i = 0; i < sequence.parameter_len && param_count < 1; i++) {
|
||||
char c = sequence.parameter[i];
|
||||
if (isDigit(c)) {
|
||||
n = (n*10) + (sequence.parameter[i] - '0');
|
||||
} else if (c == ';') {
|
||||
params[param_count++] = n;
|
||||
}
|
||||
}
|
||||
|
||||
if(n == 0)
|
||||
n = 1;
|
||||
|
||||
switch(sequence[i]) {
|
||||
case ';':
|
||||
terminated = true;
|
||||
int m = 0;
|
||||
for(size_t j = 0; j < strlen(sequence - i); j++) {
|
||||
if(sequence[j] == 'H' ||sequence[j] == 'f')
|
||||
break;
|
||||
|
||||
if(isDigit(sequence[j])) {
|
||||
m = (m*10) + sequence[j];
|
||||
} else if(j > 80) break;
|
||||
params[param_count++] = n;
|
||||
}
|
||||
|
||||
if(m == 0)
|
||||
m = 1;
|
||||
cursor_pos(n, m);
|
||||
|
||||
switch (*(sequence.final)) {
|
||||
case 'H': case 'f': // CUP - Cursor Position
|
||||
// TODO: Check to see if we have 2 paramaters
|
||||
if (params[0]) params[0]--;
|
||||
if (params[1]) params[1]--;
|
||||
set_cursor(params[0], params[1]);
|
||||
break;
|
||||
case 'A': //CUU - Cursor Up
|
||||
//cursor_pos(x, y)
|
||||
|
||||
cursor_pos(terminal_column, terminal_row + n);
|
||||
if (!params[0]) params[0] = 1;
|
||||
set_cursor(terminal_column, terminal_row - params[0]);
|
||||
break;
|
||||
case 'B': //CUD - Cursor Down
|
||||
cursor_pos(terminal_column, terminal_row - n);
|
||||
if (!params[0]) params[0] = 1;
|
||||
set_cursor(terminal_column, terminal_row + params[0]);
|
||||
break;
|
||||
case 'C': //CUF - Cursor Forward
|
||||
cursor_pos(terminal_column + n, terminal_row);
|
||||
if (!params[0]) params[0] = 1;
|
||||
set_cursor(terminal_column + params[0], terminal_row);
|
||||
break;
|
||||
case 'D': //CUB - Cursor Back
|
||||
cursor_pos(terminal_column - n, terminal_row);
|
||||
if (!params[0]) params[0] = 1;
|
||||
set_cursor(terminal_column - params[0], terminal_row);
|
||||
break;
|
||||
case 'E': //CNL - Cursor Next Line
|
||||
cursor_pos(0, terminal_row + n);
|
||||
if (!params[0]) params[0] = 1;
|
||||
set_cursor(0, terminal_row + params[0]);
|
||||
break;
|
||||
case 'F': //CPL - Cursor Previous Line
|
||||
cursor_pos(0, terminal_row - n);
|
||||
if (!params[0]) params[0] = 1;
|
||||
set_cursor(0, terminal_row - params[0]);
|
||||
break;
|
||||
case 'G': //CHA - Cursor Horizontal Absolute
|
||||
cursor_pos(n, terminal_row);
|
||||
if (params[0]) params[0]--;
|
||||
set_cursor(params[0], terminal_row);
|
||||
break;
|
||||
case 'J': //ED - Erase in Display
|
||||
//current cursor pos = y * width + x
|
||||
{
|
||||
int pos = terminal_row * 80 + terminal_column;
|
||||
if(!nPresent) {
|
||||
for(pos < (25 * 80); pos++) {
|
||||
if (params[0] == 0) { // Clear from cursor to end of screen
|
||||
for(; pos < (25 * 80); pos++) {
|
||||
vga_buffer[pos] = '\0';
|
||||
}
|
||||
n = 0;
|
||||
} else if(nPresent && n == 1) {
|
||||
for(pos > 0; pos--) {
|
||||
} else if(params[0] == 1) { // Clear from cursor to beginning
|
||||
for(; pos > 0; pos--) {
|
||||
vga_buffer[pos] = '\0';
|
||||
}
|
||||
} else if(n == 2 || n == 3) {
|
||||
} else if(params[0] == 2 || params[0] == 3) { // Clear entire screen
|
||||
// TODO: Support scrollback buffer? (n = 3)
|
||||
for(int i = 0; i < (25*80); i++) {
|
||||
vga_buffer[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'K': //EL - Erase in Line
|
||||
|
||||
{
|
||||
int pos = terminal_row * 80 + terminal_column;
|
||||
if(!nPresent) { //From cursor to end of line
|
||||
int endPos = (terminal_row + 1) * 80 - 1; //End of line = current row + 25 columns = current row + 1
|
||||
for(pos < endPos; pos++) {
|
||||
if(params[0] == 0) { // From cursor to end of line
|
||||
int endPos = (terminal_row + 1) * 80 - 1; // End of line = current row + 25 columns = current row + 1
|
||||
for(; pos < endPos; pos++) {
|
||||
vga_buffer[pos] = '\0';
|
||||
}
|
||||
} else if(nPresent && n == 1) { //From cursor to start of line
|
||||
int endPos = terminal_row * 80; //Start of line = end of previous line + 1 == current line
|
||||
for(pos > endPos; pos--) {
|
||||
} else if (params[0] == 1) { // From cursor to start of line
|
||||
int endPos = terminal_row * 80; // Start of line = end of previous line + 1 == current line
|
||||
for(; pos > endPos; pos--) {
|
||||
vga_buffer[pos] = '\0';
|
||||
}
|
||||
} else if(nPresent && n == 2) { //Entire current line
|
||||
} else if(params[0] == 2) { // Entire current line
|
||||
pos = terminal_row * 80;
|
||||
int endPos = (terminal_row + 1) * 80 - 1;
|
||||
for(pos < endPos; pos++) {
|
||||
for(; pos < endPos; pos++) {
|
||||
vga_buffer[pos] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'S': //SU - Scroll Up
|
||||
term_scroll(true);
|
||||
|
@ -240,15 +277,16 @@ void handleControlSequence(const char* sequence) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool isDigit(const char c) {
|
||||
return c == '0' || c == '1' || c == '2' || c == '3' ||
|
||||
c == '4' || c == '5' || c == '6' || c == '7' ||
|
||||
c == '8' || c == '9';
|
||||
bool isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
void set_cursor(int n, int m) {
|
||||
terminal_column = n;
|
||||
terminal_row = m;
|
||||
}
|
||||
|
||||
void term_scroll(bool down) {
|
||||
|
@ -267,6 +305,7 @@ void term_scroll(bool down) {
|
|||
term_buffer[current_pos + 160] = vga_buffer[current_pos];
|
||||
term_buffer[current_pos + 159] = vga_buffer[current_pos - 1];
|
||||
current_pos -= 2;
|
||||
}
|
||||
} else {
|
||||
while (current_pos <= 4000) {
|
||||
term_buffer[current_pos - 160 /*The character immediately below it*/] = vga_buffer[current_pos]; //Move each character up a line
|
||||
|
@ -277,18 +316,17 @@ void term_scroll(bool down) {
|
|||
|
||||
if(down) {
|
||||
current_pos = 0; //Start of first line
|
||||
for(current_pos < 80; current_pos++) {
|
||||
for(; current_pos < 80; current_pos++) {
|
||||
term_buffer[current_pos] = '\0';
|
||||
current_pos += 2;
|
||||
}
|
||||
} else {
|
||||
; //Start of the last line
|
||||
//Wipe out the bottom line
|
||||
for(current_pos = 3840; current_pos <= 3920; i++) {
|
||||
for(current_pos = 3840; current_pos <= 3920; current_pos += 2) {
|
||||
term_buffer[current_pos] = 0;
|
||||
current_pos += 2;
|
||||
}
|
||||
}
|
||||
|
||||
terminal_row = 24; //Start writing on the last line
|
||||
terminal_column = 0;
|
||||
}
|
|
@ -1,8 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <vga.h>
|
||||
|
||||
#include "vga.h"
|
||||
|
||||
struct csi_sequence {
|
||||
const char* parameter;
|
||||
size_t parameter_len;
|
||||
const char* intermediate;
|
||||
size_t intermediate_len;
|
||||
const char* final;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
|
||||
void term_setcolor(enum vga_colors);
|
||||
void screen_initialize(void);
|
||||
|
@ -11,8 +23,11 @@ void term_putchar(char);
|
|||
void term_write(const char*, size_t);
|
||||
void term_writes(const char*);
|
||||
void puts(const char*);
|
||||
void handleControlSequence(struct csi_sequence);
|
||||
void set_cursor(int, int);
|
||||
void term_scroll(void);
|
||||
void term_scroll(bool);
|
||||
void int_to_ascii(int, char*);
|
||||
void int_to_hex(int, char*);
|
||||
void empty_string(char*);
|
||||
|
||||
bool isDigit(char c);
|
||||
|
|
Loading…
Reference in New Issue
Block a user