// Terminal output /* There are a couple of bugs here and there but for now I don't care too much because this shitty implementation will be replaced one day by Flanterm (once memory management is okay: paging & kernel malloc) */ #include #include #include #include "term.h" #include "mem/misc/utils.h" extern struct boot_context boot_ctx; // Importing the PSF object file extern unsigned char _binary_zap_light16_psf_start[]; extern unsigned char _binary_zap_light16_psf_end[]; PSF1_Header* font = (PSF1_Header*)_binary_zap_light16_psf_start; uint8_t* glyphs = _binary_zap_light16_psf_start + sizeof(PSF1_Header); #define FONT_WIDTH 8 #define FONT_HEIGHT font->characterSize // Character cursor typedef struct { unsigned int x; unsigned int y; } Cursor; Cursor cursor = {0}; unsigned char* fb; struct limine_framebuffer* framebuffer; uint8_t lines_length[MAX_LINES]; int term_init() { // Get framebuffer address from Limine struct if (boot_ctx.fb) { fb = boot_ctx.fb->address; framebuffer = boot_ctx.fb; DEBUG("terminal initialized"); return 0; } return -ENOMEM; } // These are marked "static" because we don't wanna expose them all around // AKA they should just be seen here (kind of like private functions in cpp) static void putpixel(int x, int y, int color) { // Depth isn't part of limine_framebuffer attributes so it will be 4 unsigned pos = x*4 + y*framebuffer->pitch; fb[pos] = color & 255; // blue channel fb[pos+1] = (color >> 8) & 255; // green fb[pos+2] = (color >> 16) & 255; // blue } static void draw_char(char c, int px, int py, int fg, int bg) { uint8_t* glyph = glyphs + ((unsigned char)c * FONT_HEIGHT); for (size_t y=0; y> x)) ? fg : bg; putpixel(px+x, py+y, color); } } } static void erase_char(int px, int py) { for (size_t y=0; yheight / FONT_HEIGHT; } void term_scroll() { const size_t row_height = FONT_HEIGHT; const size_t row_bytes = framebuffer->pitch; const size_t screen_rows = framebuffer->height; // Move framebuffer up by one text row memmove(fb, fb + row_height * row_bytes, (screen_rows - row_height) * row_bytes); // Clear last text row size_t clear_start = (screen_rows - row_height) * row_bytes; memset(fb + clear_start, 0, row_height * row_bytes); // Shift line lengths by 1 (for backspace handling) size_t max_lines = term_max_lines(); for (size_t i = 1; i < max_lines; i++) { lines_length[i - 1] = lines_length[i]; } lines_length[max_lines - 1] = 0; if (cursor.y > 0) { cursor.y--; } } void putchar(char c) { if ((c == '\n') && ((cursor.y+1)*FONT_HEIGHT >= framebuffer->height)) { term_scroll(); return; } if (c == '\n') { lines_length[cursor.y] = cursor.x; cursor.x = 0; cursor.y++; return; } // TODO: Improperly handled. // When we're on an empty line it should get to the upper line's last character // NOT just the last position possible; we would need to track the last line's character amount for that if (c == '\b') { if (cursor.x == 0 && cursor.y == 0) { // Top-left corner return; } if (cursor.x == 0) { cursor.y--; // cursor.x = (framebuffer->width / FONT_WIDTH) -1; // here cursor.x = lines_length[cursor.y]; } else { cursor.x--; } int px = cursor.x * FONT_WIDTH; int py = cursor.y * FONT_HEIGHT; erase_char(px, py); return; } if ((cursor.x+1)*FONT_WIDTH >= framebuffer->width) { cursor.x = 0; cursor.y++; } if ((cursor.y+1)*FONT_HEIGHT >= framebuffer->height) { term_scroll(); } int px = cursor.x * FONT_WIDTH; int py = cursor.y * FONT_HEIGHT; draw_char(c, px, py, WHITE, BLACK); cursor.x++; } // Overhead that could be avoided, right? (for printf) void _putchar(char character) { putchar(character); } // Debug-printing void kputs(const char* str) { unsigned int i=0; while (str[i] != 0) { putchar(str[i]); i++; } }