Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/drivers/char/fbcon.c 3 * 4 * Copyright 2021, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/asm.h> 9 #include <fiwix/fb.h> 10 #include <fiwix/fbcon.h> 11 #include <fiwix/font.h> 12 #include <fiwix/console.h> 13 #include <fiwix/tty.h> 14 #include <fiwix/timer.h> 15 #include <fiwix/stdio.h> 16 #include <fiwix/string.h> 17 18 #define SPACE_CHAR 32 19 20 unsigned char *font_data; 21 unsigned char *cursor_shape; 22 struct video_parms video; 23 static unsigned char screen_is_off = 0; 24 25 /* RGB colors */ 26 static int color_table[] = { 27 0x000000, /* black */ 28 0x0000AA, /* blue */ 29 0x00AA00, /* green */ 30 0x00AAAA, /* cyan */ 31 0xAA0000, /* red */ 32 0xAA00AA, /* magenta */ 33 0xAA5000, /* brown */ 34 0xAAAAAA, /* gray */ 35 36 0x555555, /* dark gray */ 37 0x5555FF, /* light blue */ 38 0x55FF55, /* light green */ 39 0x55FFFF, /* light cyan */ 40 0xFF5555, /* light red */ 41 0xFF55FF, /* light magenta */ 42 0xFFFF55, /* yellow */ 43 0xFFFFFF, /* white */ 44 }; 45 46 static int get_fg_color(unsigned char color) 47 { 48 int fg, bright; 49 50 fg = color & 7; 51 bright = (color & 0xF) & 8; 52 return color_table[bright + fg]; 53 } 54 55 static int get_bg_color(unsigned char color) 56 { 57 int bg; 58 59 bg = (color >> 4) & 7; 60 return color_table[bg]; 61 } 62 63 static void set_color(void *addr, int color) 64 { 65 unsigned int *addr32; 66 unsigned short int *addr16; 67 unsigned char *addr8; 68 short int r, g, b; 69 70 switch(video.fb_bpp) { 71 case 32: 72 addr32 = (unsigned int *)addr; 73 *addr32 = color; 74 break; 75 case 24: 76 addr8 = (unsigned char *)addr; 77 *(addr8++) = color & 0xFF; 78 *(addr8++) = (color >> 8) & 0xFF; 79 *(addr8++) = (color >> 16) & 0xFF; 80 break; 81 case 16: 82 /* 0:5:6:5 */ 83 r = ((color >> 16) & 0xFF) << 8; 84 g = ((color >> 8) & 0xFF) << 8; 85 b = (color & 0xFF) << 8; 86 addr16 = (unsigned short int *)addr; 87 *addr16 = (r & 0xf800) | ((g & 0xfc00) >> 5) | ((b & 0xf800) >> 11); 88 case 15: 89 /* 1:5:5:5 */ 90 r = ((color >> 16) & 0xFF) << 8; 91 g = ((color >> 8) & 0xFF) << 8; 92 b = (color & 0xFF) << 8; 93 addr16 = (unsigned short int *)addr; 94 *addr16 = ((r & 0xf800) >> 1) | ((g & 0xf800) >> 6) | ((b & 0xf800) >> 11); 95 break; 96 } 97 } 98 99 static void draw_glyph(unsigned char *addr, int x, int y, unsigned char *ch, int color) 100 { 101 int n, b, offset; 102 103 if(screen_is_off) { 104 return; 105 } 106 107 offset = (y * video.fb_linesize) + (x * video.fb_bpp); 108 addr += offset; 109 110 for(n = 0; n < video.fb_char_height; n++) { 111 if(*(ch + n) == 0) { 112 if(ch == cursor_shape) { 113 addr += video.fb_pitch; 114 continue; 115 } 116 b = video.fb_char_width - 1; 117 do { 118 set_color(addr, get_bg_color(color)); 119 addr += video.fb_pixelwidth; 120 b--; 121 } while(b >= 0); 122 } else { 123 b = video.fb_char_width - 1; 124 do { 125 if(*(ch + n) & (1 << b)) { 126 set_color(addr, get_fg_color(color)); 127 } else { 128 set_color(addr, get_bg_color(color)); 129 } 130 addr += video.fb_pixelwidth; 131 b--; 132 } while(b >= 0); 133 } 134 addr += (video.fb_width - video.fb_char_width) * video.fb_pixelwidth; 135 } 136 } 137 138 static void remove_cursor(struct vconsole *vc) 139 { 140 int soffset; 141 unsigned char *vidmem, *ch; 142 short int *screen, sch; 143 144 vidmem = vc->vidmem; 145 screen = vc->screen; 146 soffset = (vc->cursor_y * vc->columns) + vc->cursor_x; 147 148 sch = screen[soffset]; 149 if(sch & 0xFF) { 150 ch = &font_data[(sch & 0xFF) * video.fb_char_height]; 151 } else { 152 ch = &font_data[SPACE_CHAR * video.fb_char_height]; 153 } 154 draw_glyph(vidmem, vc->cursor_x, vc->cursor_y, ch, sch >> 8); 155 } 156 157 static void draw_cursor(struct vconsole *vc) 158 { 159 unsigned char *vidmem; 160 161 vidmem = vc->vidmem; 162 draw_glyph(vidmem, vc->x, vc->y, cursor_shape, DEF_MODE >> 8); 163 } 164 165 void fbcon_put_char(struct vconsole *vc, unsigned char ch) 166 { 167 short int *screen; 168 unsigned char *vidmem; 169 170 screen = vc->screen; 171 172 if(!(vc->flags & CONSOLE_HAS_FOCUS)) { 173 screen[(vc->y * vc->columns) + vc->x] = vc->color_attr | ch; 174 return; 175 } 176 177 vidmem = vc->vidmem; 178 draw_glyph(vidmem, vc->x, vc->y, &font_data[ch * video.fb_char_height], vc->color_attr >> 8); 179 screen[(vc->y * vc->columns) + vc->x] = vc->color_attr | ch; 180 vcbuf[(video.buf_y * vc->columns) + vc->x] = vc->color_attr | ch; 181 } 182 183 void fbcon_insert_char(struct vconsole *vc) 184 { 185 int n, soffset; 186 short int tmp, slast_ch; 187 unsigned char *vidmem, *last_ch; 188 short int *screen; 189 190 vidmem = vc->vidmem; 191 screen = vc->screen; 192 soffset = (vc->y * vc->columns) + vc->x; 193 n = vc->x; 194 last_ch = &font_data[SPACE_CHAR * video.fb_char_height]; 195 slast_ch = BLANK_MEM; 196 197 while(n < vc->columns) { 198 tmp = screen[soffset]; 199 if(vc->flags & CONSOLE_HAS_FOCUS) { 200 draw_glyph(vidmem, n, vc->y, last_ch, vc->color_attr >> 8); 201 last_ch = &font_data[(tmp & 0xFF) * video.fb_char_height]; 202 } 203 memset_w(screen + soffset, slast_ch, 1); 204 slast_ch = tmp; 205 soffset++; 206 n++; 207 } 208 } 209 210 void fbcon_delete_char(struct vconsole *vc) 211 { 212 int n, soffset; 213 short int sch; 214 unsigned char *vidmem, *ch; 215 short int *screen; 216 217 vidmem = vc->vidmem; 218 screen = vc->screen; 219 soffset = (vc->y * vc->columns) + vc->x; 220 n = vc->x; 221 222 while(n < vc->columns) { 223 sch = screen[soffset + 1]; 224 if(vc->flags & CONSOLE_HAS_FOCUS) { 225 if(sch & 0xFF) { 226 ch = &font_data[(sch & 0xFF) * video.fb_char_height]; 227 } else { 228 ch = &font_data[SPACE_CHAR * video.fb_char_height]; 229 } 230 draw_glyph(vidmem, n, vc->y, ch, vc->color_attr >> 8); 231 } 232 memset_w(screen + soffset, sch, 1); 233 soffset++; 234 n++; 235 } 236 memset_w(screen + soffset, BLANK_MEM, 1); 237 } 238 239 void fbcon_update_curpos(struct vconsole *vc) 240 { 241 if(!(vc->flags & CONSOLE_HAS_FOCUS)) { 242 return; 243 } 244 245 /* remove old cursor */ 246 if(vc->x != vc->cursor_x || vc->y != vc->cursor_y) { 247 remove_cursor(vc); 248 } 249 250 if(video.flags & VPF_CURSOR_ON) { 251 draw_cursor(vc); 252 } 253 vc->cursor_x = vc->x; 254 vc->cursor_y = vc->y; 255 } 256 257 void fbcon_show_cursor(struct vconsole *vc, int mode) 258 { 259 switch(mode) { 260 case COND: 261 if(!(video.flags & VPF_CURSOR_ON)) { 262 break; 263 } 264 /* fall through */ 265 case ON: 266 video.flags |= VPF_CURSOR_ON; 267 fbcon_update_curpos(vc); 268 break; 269 case OFF: 270 video.flags &= ~VPF_CURSOR_ON; 271 fbcon_update_curpos(vc); 272 break; 273 } 274 } 275 276 void fbcon_get_curpos(struct vconsole *vc) 277 { 278 /* not used */ 279 } 280 281 void fbcon_write_screen(struct vconsole *vc, int from, int count, short int color) 282 { 283 int n, n2, lines, columns, x, y; 284 unsigned char *vidmem, *ch; 285 short int *screen; 286 287 screen = vc->screen; 288 if(!(vc->flags & CONSOLE_HAS_FOCUS)) { 289 memset_w(screen + from, color, count); 290 return; 291 } 292 293 vidmem = vc->vidmem; 294 ch = &font_data[SPACE_CHAR * video.fb_char_height]; 295 x = from % vc->columns; 296 y = from / vc->columns; 297 lines = count / vc->columns; 298 columns = x + count; 299 if(!lines) { 300 lines = 1; 301 } 302 if(!columns) { 303 columns = vc->columns; 304 } 305 for(n = 0; n < lines; n++) { 306 for(n2 = x; n2 < columns; n2++) { 307 draw_glyph(vidmem, n2, y + n, ch, color >> 8); 308 } 309 x = 0; 310 columns = vc->columns; 311 } 312 memset_w(screen + from, color, count); 313 } 314 315 void fbcon_blank_screen(struct vconsole *vc) 316 { 317 unsigned char *vidmem; 318 319 if(vc->flags & CONSOLE_BLANKED) { 320 return; 321 } 322 323 vidmem = vc->vidmem; 324 if(!(int)vidmem) { 325 return; 326 } 327 328 memset_b(vidmem, 0, video.fb_size); 329 vc->flags |= CONSOLE_BLANKED; 330 } 331 332 void fbcon_scroll_screen(struct vconsole *vc, int top, int mode) 333 { 334 int soffset, poffset, count; 335 int x, y; 336 short int *screen, sch, pch; 337 unsigned char *vidmem, *ch; 338 339 vidmem = vc->vidmem; 340 screen = vc->screen; 341 342 if(!top) { 343 top = vc->top; 344 } 345 switch(mode) { 346 case SCROLL_UP: 347 if(vc->flags & CONSOLE_HAS_FOCUS) { 348 for(y = top + 1; y < vc->lines; y++) { 349 for(x = 0; x < vc->columns; x++) { 350 soffset = (y * vc->columns) + x; 351 poffset = ((y - 1) * vc->columns) + x; 352 sch = screen[soffset]; 353 pch = screen[poffset]; 354 if(sch == pch) { 355 continue; 356 } 357 if(sch & 0xFF) { 358 ch = &font_data[(sch & 0xFF) * video.fb_char_height]; 359 } else { 360 ch = &font_data[SPACE_CHAR * video.fb_char_height]; 361 } 362 draw_glyph(vidmem, x, y - 1, ch, sch >> 8); 363 } 364 } 365 if(!screen_is_off) { 366 count = video.fb_pitch * video.fb_char_height; 367 memset_l(vidmem + video.fb_vsize - count, 0, count / sizeof(unsigned int)); 368 } 369 } 370 count = vc->columns * (vc->lines - top - 1); 371 soffset = top * vc->columns; 372 top = (top + 1) * vc->columns; 373 if(vc->cursor_y) { 374 vc->cursor_y--; 375 } 376 memcpy_w(screen + soffset, screen + top, count); 377 memset_w(screen + soffset + count, BLANK_MEM, top); 378 break; 379 case SCROLL_DOWN: 380 for(y = vc->lines - 2; y >= top; y--) { 381 for(x = 0; x < vc->columns; x++) { 382 if(vc->flags & CONSOLE_HAS_FOCUS) { 383 soffset = (y * vc->columns) + x; 384 poffset = ((y + 1) * vc->columns) + x; 385 sch = screen[soffset]; 386 pch = screen[poffset]; 387 if(sch == pch) { 388 continue; 389 } 390 if(sch & 0xFF) { 391 ch = &font_data[(sch & 0xFF) * video.fb_char_height]; 392 } else { 393 ch = &font_data[SPACE_CHAR * video.fb_char_height]; 394 } 395 draw_glyph(vidmem, x, y + 1, ch, sch >> 8); 396 } 397 } 398 memcpy_w(screen + (vc->columns * (y + 1)), screen + (vc->columns * y), vc->columns); 399 } 400 if((vc->flags & CONSOLE_HAS_FOCUS) && !screen_is_off) { 401 count = video.fb_pitch * video.fb_char_height; 402 memset_l(vidmem + (top * count), 0, count / sizeof(unsigned int)); 403 } 404 memset_w(screen + (top * vc->columns), BLANK_MEM, vc->columns); 405 break; 406 } 407 return; 408 } 409 410 void fbcon_restore_screen(struct vconsole *vc) 411 { 412 int x, y; 413 short int *screen, sch; 414 unsigned char *vidmem, *ch, c; 415 416 vidmem = vc->vidmem; 417 screen = vc->screen; 418 419 if(!screen_is_off && !video.buf_top) { 420 memset_b(vidmem, 0, video.fb_size); 421 } 422 for(y = 0; y < video.lines; y++) { 423 for(x = 0; x < vc->columns; x++) { 424 sch = screen[(y * vc->columns) + x]; 425 c = sch & 0xFF; 426 if(!c || (c == SPACE_CHAR && !(sch >> 8))) { 427 continue; 428 } 429 ch = &font_data[c * video.fb_char_height]; 430 draw_glyph(vidmem, x, y, ch, sch >> 8); 431 } 432 } 433 vc->flags &= ~CONSOLE_BLANKED; 434 } 435 436 void fbcon_screen_on(struct vconsole *vc) 437 { 438 unsigned long int flags; 439 struct callout_req creq; 440 441 if(screen_is_off) { 442 screen_is_off = 0; 443 SAVE_FLAGS(flags); CLI(); 444 fbcon_restore_screen(vc); 445 fbcon_update_curpos(vc); 446 RESTORE_FLAGS(flags); 447 vc->flags &= ~CONSOLE_BLANKED; 448 } 449 450 if(BLANK_INTERVAL) { 451 creq.fn = fbcon_screen_off; 452 creq.arg = (unsigned int)vc; 453 add_callout(&creq, BLANK_INTERVAL); 454 } 455 } 456 457 void fbcon_screen_off(unsigned int arg) 458 { 459 struct vconsole *vc; 460 unsigned long int flags; 461 462 vc = (struct vconsole *)arg; 463 screen_is_off = 1; 464 SAVE_FLAGS(flags); CLI(); 465 fbcon_blank_screen(vc); 466 RESTORE_FLAGS(flags); 467 } 468 469 void fbcon_buf_scroll(struct vconsole *vc, int mode) 470 { 471 short int sch; 472 int y, x, offset; 473 unsigned char *vidmem, *ch; 474 475 if(video.buf_y <= SCREEN_LINES) { 476 return; 477 } 478 479 vidmem = vc->vidmem; 480 481 if(mode == SCROLL_UP) { 482 if(video.buf_top < 0) { 483 return; 484 } 485 if(!video.buf_top) { 486 video.buf_top = (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS; 487 } 488 video.buf_top -= (SCREEN_LINES / 2) * SCREEN_COLS; 489 if(video.buf_top < 0) { 490 video.buf_top = 0; 491 } 492 for(offset = 0, y = 0; y < video.lines; y++) { 493 for(x = 0; x < vc->columns; x++, offset++) { 494 sch = vcbuf[video.buf_top + offset]; 495 if(sch & 0xFF) { 496 ch = &font_data[(sch & 0xFF) * video.fb_char_height]; 497 } else { 498 ch = &font_data[SPACE_CHAR * video.fb_char_height]; 499 } 500 draw_glyph(vidmem, x, y, ch, sch >> 8); 501 } 502 } 503 if(!video.buf_top) { 504 video.buf_top = -1; 505 } 506 fbcon_show_cursor(vc, OFF); 507 return; 508 } 509 if(mode == SCROLL_DOWN) { 510 if(!video.buf_top) { 511 return; 512 } 513 if(video.buf_top == video.buf_y * SCREEN_COLS) { 514 return; 515 } 516 if(video.buf_top < 0) { 517 video.buf_top = 0; 518 } 519 video.buf_top += (SCREEN_LINES / 2) * SCREEN_COLS; 520 if(video.buf_top >= (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS) { 521 video.buf_top = (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS; 522 } 523 for(offset = 0, y = 0; y < video.lines; y++) { 524 for(x = 0; x < vc->columns; x++, offset++) { 525 sch = vcbuf[video.buf_top + offset]; 526 if(sch & 0xFF) { 527 ch = &font_data[(sch & 0xFF) * video.fb_char_height]; 528 } else { 529 ch = &font_data[SPACE_CHAR * video.fb_char_height]; 530 } 531 draw_glyph(vidmem, x, y, ch, sch >> 8); 532 } 533 } 534 if(video.buf_top >= (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS) { 535 fbcon_show_cursor(vc, ON); 536 fbcon_update_curpos(vc); 537 } 538 return; 539 } 540 } 541 542 void fbcon_cursor_blink(unsigned int arg) 543 { 544 struct vconsole *vc; 545 struct callout_req creq; 546 static int blink_on = 0; 547 548 vc = (struct vconsole *)arg; 549 if(!(vc->flags & CONSOLE_HAS_FOCUS)) { 550 return; 551 } 552 553 if(video.flags & VPF_CURSOR_ON && !screen_is_off) { 554 if(blink_on) { 555 draw_cursor(vc); 556 } else { 557 remove_cursor(vc); 558 } 559 } 560 blink_on = !blink_on; 561 creq.fn = fbcon_cursor_blink; 562 creq.arg = arg; 563 add_callout(&creq, 25); /* 250ms */ 564 } 565 566 void fbcon_init(void) 567 { 568 struct fbcon_font_desc *font_desc; 569 570 video.put_char = fbcon_put_char; 571 video.insert_char = fbcon_insert_char; 572 video.delete_char = fbcon_delete_char; 573 video.update_curpos = fbcon_update_curpos; 574 video.show_cursor = fbcon_show_cursor; 575 video.get_curpos = fbcon_get_curpos; 576 video.write_screen = fbcon_write_screen; 577 video.blank_screen = fbcon_blank_screen; 578 video.scroll_screen = fbcon_scroll_screen; 579 video.restore_screen = fbcon_restore_screen; 580 video.screen_on = fbcon_screen_on; 581 video.buf_scroll = fbcon_buf_scroll; 582 video.cursor_blink = fbcon_cursor_blink; 583 584 if(!(font_desc = fbcon_find_font(video.fb_char_height))) { 585 font_desc = fbcon_find_font(16); 586 } 587 font_data = font_desc->data; 588 cursor_shape = font_desc->cursorshape; 589 }