framebuffer.c (9992B)
1 #include <drivers/video/framebuffer.h> 2 #include <klibc/string.h> 3 #include <stddef.h> 4 #include <stdarg.h> 5 #include <limine_requests.h> 6 #include <drivers/video/font8x16.h> 7 8 static fb_console_t console; 9 10 void 11 fb_init(void) 12 { 13 if (framebuffer_request.response == NULL || 14 framebuffer_request.response->framebuffer_count < 1) { 15 return; 16 } 17 18 console.fb = framebuffer_request.response->framebuffers[0]; 19 console.fg_color = 0xFFFFFF; // White 20 console.bg_color = 0x000000; // Black 21 console.cursor_x = 0; 22 console.cursor_y = 0; 23 console.cols = console.fb->width / FB_CHAR_WIDTH; 24 console.rows = console.fb->height / FB_CHAR_HEIGHT; 25 26 fb_clear(); 27 } 28 29 void 30 fb_clear(void) 31 { 32 if (!console.fb) return; 33 34 uint32_t *fb = (uint32_t *)console.fb->address; 35 size_t pixels = (console.fb->pitch / 4) * console.fb->height; 36 37 for (size_t i = 0; i < pixels; i++) { 38 fb[i] = console.bg_color; 39 } 40 41 console.cursor_x = 0; 42 console.cursor_y = 0; 43 } 44 45 void 46 fb_draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color) 47 { 48 if (!console.fb) return; 49 50 uint32_t *fb = (uint32_t *)console.fb->address; 51 uint32_t pitch_pixels = console.fb->pitch / 4; 52 53 for (uint32_t row = y; row < y + h && row < console.fb->height; row++) { 54 for (uint32_t col = x; col < x + w && col < console.fb->width; col++) { 55 fb[row * pitch_pixels + col] = color; 56 } 57 } 58 } 59 60 void 61 fb_set_color(uint32_t fg, uint32_t bg) 62 { 63 console.fg_color = fg; 64 console.bg_color = bg; 65 } 66 67 static void 68 draw_char(uint32_t x, uint32_t y, char c, uint32_t fg_color, uint32_t bg_color) 69 { 70 if (!console.fb) return; 71 72 uint32_t *fb = (uint32_t *)console.fb->address; 73 uint32_t pitch_pixels = console.fb->pitch / 4; 74 75 // Get the font data for this character 76 const uint8_t *char_data = &IBM_VGA_8x16[c * 16]; 77 78 // Draw each row of the character 79 for (uint32_t row = 0; row < FB_CHAR_HEIGHT; row++) { 80 uint8_t font_row = char_data[row]; 81 82 // Draw each pixel in the row 83 for (uint32_t col = 0; col < FB_CHAR_WIDTH; col++) { 84 uint32_t px = x + col; 85 uint32_t py = y + row; 86 87 // Check bounds 88 if (px >= console.fb->width || py >= console.fb->height) continue; 89 90 // Check if this pixel should be set (bit test) 91 if (font_row & (0x80 >> col)) { 92 fb[py * pitch_pixels + px] = fg_color; 93 } else { 94 fb[py * pitch_pixels + px] = bg_color; 95 } 96 } 97 } 98 } 99 100 static void 101 scroll_up(void) 102 { 103 if (!console.fb) return; 104 105 uint32_t *fb = (uint32_t *)console.fb->address; 106 uint32_t pitch_pixels = console.fb->pitch / 4; 107 108 // Move all lines up by one character height 109 for (uint32_t y = 0; y < console.fb->height - FB_CHAR_HEIGHT; y++) { 110 for (uint32_t x = 0; x < console.fb->width; x++) { 111 fb[y * pitch_pixels + x] = fb[(y + FB_CHAR_HEIGHT) * pitch_pixels + x]; 112 } 113 } 114 115 // Clear the last line 116 for (uint32_t y = console.fb->height - FB_CHAR_HEIGHT; y < console.fb->height; y++) { 117 for (uint32_t x = 0; x < console.fb->width; x++) { 118 fb[y * pitch_pixels + x] = console.bg_color; 119 } 120 } 121 } 122 123 void 124 fb_putchar(char c) 125 { 126 if (!console.fb) return; 127 128 // Handle special characters 129 switch (c) { 130 case '\n': 131 console.cursor_x = 0; 132 console.cursor_y++; 133 break; 134 case '\r': 135 console.cursor_x = 0; 136 break; 137 case '\t': 138 console.cursor_x = (console.cursor_x + 8) & ~7; 139 break; 140 case '\b': 141 if (console.cursor_x > 0) { 142 console.cursor_x--; 143 draw_char(console.cursor_x * FB_CHAR_WIDTH, 144 console.cursor_y * FB_CHAR_HEIGHT, 145 ' ', console.fg_color, console.bg_color); 146 } 147 break; 148 default: 149 // Draw the character 150 draw_char(console.cursor_x * FB_CHAR_WIDTH, 151 console.cursor_y * FB_CHAR_HEIGHT, 152 c, console.fg_color, console.bg_color); 153 console.cursor_x++; 154 break; 155 } 156 157 // Handle line wrapping 158 if (console.cursor_x >= console.cols) { 159 console.cursor_x = 0; 160 console.cursor_y++; 161 } 162 163 // Handle scrolling 164 if (console.cursor_y >= console.rows) { 165 scroll_up(); 166 console.cursor_y = console.rows - 1; 167 } 168 } 169 170 void 171 fb_puts(const char *str) 172 { 173 while (*str) { 174 fb_putchar(*str++); 175 } 176 } 177 178 static void 179 print_number(unsigned long num, int base, int width, char pad) 180 { 181 char digits[] = "0123456789ABCDEF"; 182 char buffer[32]; 183 int pos = 0; 184 185 // Convert number to string (reversed) 186 if (num == 0) { 187 buffer[pos++] = '0'; 188 } else { 189 while (num > 0) { 190 buffer[pos++] = digits[num % base]; 191 num /= base; 192 } 193 } 194 195 // Add padding if needed 196 while (pos < width) { 197 buffer[pos++] = pad; 198 } 199 200 // Print reversed 201 while (pos > 0) { 202 fb_putchar(buffer[--pos]); 203 } 204 } 205 206 static void 207 print_signed(long num, int width, char pad) 208 { 209 if (num < 0) { 210 fb_putchar('-'); 211 print_number(-num, 10, width, pad); 212 } else { 213 print_number(num, 10, width, pad); 214 } 215 } 216 217 void 218 fb_printf(const char *fmt, ...) 219 { 220 va_list args; 221 va_start(args, fmt); 222 223 while (*fmt) { 224 if (*fmt == '%') { 225 fmt++; 226 227 // Handle width and padding 228 char pad = ' '; 229 int width = 0; 230 231 if (*fmt == '0') { 232 pad = '0'; 233 fmt++; 234 } 235 236 while (*fmt >= '0' && *fmt <= '9') { 237 width = width * 10 + (*fmt - '0'); 238 fmt++; 239 } 240 241 // Handle format specifiers 242 switch (*fmt) { 243 case 'd': 244 case 'i': 245 print_signed(va_arg(args, int), width, pad); 246 break; 247 248 case 'u': 249 print_number(va_arg(args, unsigned int), 10, width, pad); 250 break; 251 252 case 'x': 253 print_number(va_arg(args, unsigned int), 16, width, pad); 254 break; 255 256 case 'X': 257 print_number(va_arg(args, unsigned int), 16, width, pad); 258 break; 259 260 case 'p': 261 fb_puts("0x"); 262 print_number((unsigned long)va_arg(args, void*), 16, 16, '0'); 263 break; 264 265 case 'c': 266 fb_putchar(va_arg(args, int)); 267 break; 268 269 case 's': 270 { 271 const char *str = va_arg(args, const char*); 272 if (str) { 273 fb_puts(str); 274 } else { 275 fb_puts("(null)"); 276 } 277 } 278 break; 279 280 case '%': 281 fb_putchar('%'); 282 break; 283 284 default: 285 fb_putchar('%'); 286 fb_putchar(*fmt); 287 break; 288 } 289 } else { 290 fb_putchar(*fmt); 291 } 292 fmt++; 293 } 294 295 va_end(args); 296 }