#include <cursor.h> #include <terminal.h> #include <util.h> uint16_t *framebuffer = (uint16_t *)FRAMEBUFFER_LOCATION; uint8_t currentFormat = 0x0A; void setTextStyle(uint8_t style) { currentFormat = style; } void setCharAtOffset(uint16_t offset, char c, uint8_t format) { *((uint16_t *)FRAMEBUFFER_LOCATION + offset) = format << 8 | c; } uint16_t getOffset(uint8_t x, uint8_t y) { return VIDEO_WIDTH * y + x; } void clearScreen() { setCursorOffset(0); for (int y = 0; y < VIDEO_HEIGHT; y++) { for (int x = 0; x < VIDEO_WIDTH; x++) { setCharAtOffset(getOffset(x, y), ' ', currentFormat); } } } void shiftUp() { for (int y = 1; y < VIDEO_HEIGHT; y++) { for (int x = 0; x < VIDEO_WIDTH; x++) { uint16_t old = *(uint16_t *)(FRAMEBUFFER_LOCATION + getOffset(x, y) * 2); setCharAtOffset(getOffset(x, y - 1), old, old >> 8); } } for (int x = 0; x < VIDEO_WIDTH; x++) { setCharAtOffset(getOffset(x, VIDEO_HEIGHT - 1), ' ', currentFormat); } } void shiftDown() { for (int y = VIDEO_HEIGHT - 1; y >= 0; y--) { for (int x = 0; x < VIDEO_WIDTH; x++) { uint16_t old = *(uint16_t *)(FRAMEBUFFER_LOCATION + getOffset(x, y) * 2); setCharAtOffset(getOffset(x, y + 1), old, old >> 8); } } for (int x = 0; x < VIDEO_WIDTH; x++) { setCharAtOffset(x, ' ', currentFormat); } } uint16_t cursorOffset = 0; void newLine() { uint8_t x = cursorOffset % VIDEO_WIDTH; uint8_t y = cursorOffset / VIDEO_WIDTH; x = 0; y++; if (y >= VIDEO_HEIGHT) { shiftUp(); y--; } cursorOffset = getOffset(x, y); setCursorOffset(cursorOffset); } void clearLine() { uint8_t y = cursorOffset / VIDEO_WIDTH; for (int x = 0; x < VIDEO_WIDTH; x++) { setCharAtOffset(getOffset(x, y), ' ', currentFormat); } } typedef enum { STANDARD, ANSI_ESCAPE } States; typedef enum { BRACKET_OPEN, BUFFER } AnsiEscapeStates; States currentState = STANDARD; AnsiEscapeStates currentAnsiEscapeState = BRACKET_OPEN; uint32_t ansiEscapeBuffer1 = 0; uint32_t ansiEscapeBuffer2 = 0; uint32_t *currentBuffer = &ansiEscapeBuffer1; uint32_t savedCursor = 0; #define addBuffer(i) \ case i + '0': \ *currentBuffer *= 10; \ *currentBuffer += i; \ break; #define forCurrentBuffer(func) \ for (uint16_t i = 0; i < max(*currentBuffer, 1); i++) \ func void writeChar(char c) { if (c == 0) return; switch (currentState) { case STANDARD: if (c == '\x1B') { currentState = ANSI_ESCAPE; currentAnsiEscapeState = BRACKET_OPEN; } break; case ANSI_ESCAPE: switch (currentAnsiEscapeState) { case BRACKET_OPEN: currentAnsiEscapeState = BUFFER; currentBuffer = &ansiEscapeBuffer1; ansiEscapeBuffer1 = 0; ansiEscapeBuffer2 = 0; return; case BUFFER: switch (c) { addBuffer(0); addBuffer(1); addBuffer(2); addBuffer(3); addBuffer(4); addBuffer(5); addBuffer(6); addBuffer(7); addBuffer(8); addBuffer(9); case 'H': cursorOffset = 0; setCursorOffset(0); currentState = STANDARD; return; case 'J': clearScreen(); currentState = STANDARD; return; case 'A': forCurrentBuffer({ if (cursorOffset < VIDEO_WIDTH) { shiftDown(); } else { cursorOffset -= VIDEO_WIDTH; } }); setCursorOffset(cursorOffset); currentState = STANDARD; return; case 'B': forCurrentBuffer({ if (cursorOffset / VIDEO_WIDTH + 1 >= VIDEO_HEIGHT) { shiftUp(); } else { cursorOffset += VIDEO_WIDTH; setCursorOffset(cursorOffset); } }); currentState = STANDARD; return; case 'C': forCurrentBuffer({ if ((cursorOffset + 1) / VIDEO_WIDTH >= VIDEO_HEIGHT) { newLine(); } else { cursorOffset++; setCursorOffset(cursorOffset); } }); currentState = STANDARD; return; case 'D': forCurrentBuffer({ if (cursorOffset == 0) { cursorOffset = VIDEO_WIDTH - 1; shiftDown(); } else { cursorOffset--; } }); setCursorOffset(cursorOffset); currentState = STANDARD; return; case 's': savedCursor = cursorOffset; currentState = STANDARD; return; case 'u': cursorOffset = savedCursor; setCursorOffset(cursorOffset); currentState = STANDARD; return; case 'k': setCursorOffset(getOffset(0, cursorOffset / VIDEO_WIDTH)); clearLine(); currentState = STANDARD; return; } } } if (currentState != STANDARD) { return; } switch (c) { case '\n': newLine(); return; case '\b': // savely move cursor left via ANSI-escape codes writeChar(0x1B); writeChar('['); writeChar('D'); setCharAtOffset(cursorOffset, ' ', currentFormat); return; } setCharAtOffset(cursorOffset++, c, currentFormat); setCursorOffset(cursorOffset); }