1 /* 2 * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 10 #include <Drivers.h> 11 #include <KernelExport.h> 12 13 #include <console.h> 14 #include <lock.h> 15 16 #include <string.h> 17 #include <stdio.h> 18 19 20 #define DEVICE_NAME "console" 21 22 #define TAB_SIZE 8 23 #define TAB_MASK 7 24 25 #define FMASK 0x0f 26 #define BMASK 0x70 27 28 typedef enum { 29 CONSOLE_STATE_NORMAL = 0, 30 CONSOLE_STATE_GOT_ESCAPE, 31 CONSOLE_STATE_SEEN_BRACKET, 32 CONSOLE_STATE_NEW_ARG, 33 CONSOLE_STATE_PARSING_ARG, 34 } console_state; 35 36 typedef enum { 37 SCREEN_ERASE_WHOLE, 38 SCREEN_ERASE_UP, 39 SCREEN_ERASE_DOWN 40 } erase_screen_mode; 41 42 typedef enum { 43 LINE_ERASE_WHOLE, 44 LINE_ERASE_LEFT, 45 LINE_ERASE_RIGHT 46 } erase_line_mode; 47 48 #define MAX_ARGS 8 49 50 static struct console_desc { 51 mutex lock; 52 53 int32 lines; 54 int32 columns; 55 56 uint8 attr; 57 uint8 saved_attr; 58 bool bright_attr; 59 bool reverse_attr; 60 61 int32 x; /* current x coordinate */ 62 int32 y; /* current y coordinate */ 63 int32 saved_x; /* used to save x and y */ 64 int32 saved_y; 65 66 int32 scroll_top; /* top of the scroll region */ 67 int32 scroll_bottom; /* bottom of the scroll region */ 68 69 /* state machine */ 70 console_state state; 71 int32 arg_ptr; 72 int32 args[MAX_ARGS]; 73 74 char module_name[B_FILE_NAME_LENGTH]; 75 console_module_info *module; 76 } gconsole; 77 78 int32 api_version = B_CUR_DRIVER_API_VERSION; 79 80 81 static inline void 82 update_cursor(struct console_desc *con, int x, int y) 83 { 84 con->module->move_cursor(x, y); 85 } 86 87 88 static void 89 gotoxy(struct console_desc *con, int new_x, int new_y) 90 { 91 if (new_x >= con->columns) 92 new_x = con->columns - 1; 93 if (new_x < 0) 94 new_x = 0; 95 if (new_y >= con->lines) 96 new_y = con->lines - 1; 97 if (new_y < 0) 98 new_y = 0; 99 100 con->x = new_x; 101 con->y = new_y; 102 } 103 104 105 static void 106 reset_console(struct console_desc *con) 107 { 108 con->attr = 0x0f; 109 con->scroll_top = 0; 110 con->scroll_bottom = con->lines - 1; 111 con->bright_attr = true; 112 con->reverse_attr = false; 113 } 114 115 116 /** scroll from the cursor line up to the top of the scroll region up one line */ 117 118 static void 119 scrup(struct console_desc *con) 120 { 121 // see if cursor is outside of scroll region 122 if (con->y < con->scroll_top || con->y > con->scroll_bottom) 123 return; 124 125 if (con->y - con->scroll_top > 1) { 126 // move the screen up one 127 con->module->blit(0, con->scroll_top + 1, con->columns, 128 con->y - con->scroll_top, 0, con->scroll_top); 129 } 130 131 // clear the bottom line 132 con->module->fill_glyph(0, con->y, con->columns, 1, ' ', con->attr); 133 } 134 135 136 /** scroll from the cursor line down to the bottom of the scroll region down one line */ 137 138 static void 139 scrdown(struct console_desc *con) 140 { 141 // see if cursor is outside of scroll region 142 if (con->y < con->scroll_top || con->y > con->scroll_bottom) 143 return; 144 145 if (con->scroll_bottom - con->y > 1) { 146 // move the screen down one 147 con->module->blit(0, con->y, con->columns, con->scroll_bottom - con->y, 0, con->y + 1); 148 } 149 150 // clear the top line 151 con->module->fill_glyph(0, con->y, con->columns, 1, ' ', con->attr); 152 } 153 154 155 static void 156 lf(struct console_desc *con) 157 { 158 //dprintf("lf: y %d x %d scroll_top %d scoll_bottom %d\n", con->y, con->x, con->scroll_top, con->scroll_bottom); 159 160 if (con->y == con->scroll_bottom ) { 161 // we hit the bottom of our scroll region 162 scrup(con); 163 } else if(con->y < con->scroll_bottom) { 164 con->y++; 165 } 166 } 167 168 169 static void 170 rlf(struct console_desc *con) 171 { 172 if (con->y == con->scroll_top) { 173 // we hit the top of our scroll region 174 scrdown(con); 175 } else if (con->y > con->scroll_top) { 176 con->y--; 177 } 178 } 179 180 181 static void 182 cr(struct console_desc *con) 183 { 184 con->x = 0; 185 } 186 187 188 static void 189 del(struct console_desc *con) 190 { 191 if (con->x > 0) { 192 con->x--; 193 } else if (con->y > 0) { 194 con->y--; 195 con->x = con->columns - 1; 196 } else { 197 //This doesn't work... 198 //scrdown(con); 199 //con->y--; 200 //con->x = con->columns - 1; 201 return; 202 } 203 con->module->put_glyph(con->x, con->y, ' ', con->attr); 204 } 205 206 207 static void 208 erase_line(struct console_desc *con, erase_line_mode mode) 209 { 210 switch (mode) { 211 case LINE_ERASE_WHOLE: 212 con->module->fill_glyph(0, con->y, con->columns, 1, ' ', con->attr); 213 break; 214 case LINE_ERASE_LEFT: 215 con->module->fill_glyph(0, con->y, con->x+1, 1, ' ', con->attr); 216 break; 217 case LINE_ERASE_RIGHT: 218 con->module->fill_glyph(con->x, con->y, con->columns - con->x, 1, ' ', con->attr); 219 break; 220 default: 221 return; 222 } 223 } 224 225 226 static void 227 erase_screen(struct console_desc *con, erase_screen_mode mode) 228 { 229 switch (mode) { 230 case SCREEN_ERASE_WHOLE: 231 con->module->fill_glyph(0, 0, con->columns, con->lines, ' ', con->attr); 232 break; 233 case SCREEN_ERASE_UP: 234 con->module->fill_glyph(0, 0, con->columns, con->y + 1, ' ', con->attr); 235 break; 236 case SCREEN_ERASE_DOWN: 237 con->module->fill_glyph(con->y, 0, con->columns, con->lines - con->y, ' ', con->attr); 238 break; 239 default: 240 return; 241 } 242 } 243 244 245 static void 246 save_cur(struct console_desc *con, bool save_attrs) 247 { 248 con->saved_x = con->x; 249 con->saved_y = con->y; 250 if (save_attrs) 251 con->saved_attr = con->attr; 252 } 253 254 255 static void 256 restore_cur(struct console_desc *con, bool restore_attrs) 257 { 258 con->x = con->saved_x; 259 con->y = con->saved_y; 260 if (restore_attrs) 261 con->attr = con->saved_attr; 262 } 263 264 265 static char 266 console_putch(struct console_desc *con, const char c) 267 { 268 if (++con->x >= con->columns) { 269 cr(con); 270 lf(con); 271 } 272 con->module->put_glyph(con->x-1, con->y, c, con->attr); 273 return c; 274 } 275 276 277 static void 278 tab(struct console_desc *con) 279 { 280 con->x = (con->x + TAB_SIZE) & ~TAB_MASK; 281 if (con->x >= con->columns) { 282 con->x -= con->columns; 283 lf(con); 284 } 285 } 286 287 288 static void 289 set_scroll_region(struct console_desc *con, int top, int bottom) 290 { 291 if (top < 0) 292 top = 0; 293 if (bottom >= con->lines) 294 bottom = con->lines - 1; 295 if (top > bottom) 296 return; 297 298 con->scroll_top = top; 299 con->scroll_bottom = bottom; 300 } 301 302 303 static void 304 set_vt100_attributes(struct console_desc *con, int32 *args, int32 argCount) 305 { 306 for (int32 i = 0; i < argCount; i++) { 307 //dprintf("set_vt100_attributes: %ld\n", args[i]); 308 switch (args[i]) { 309 case 0: // reset 310 con->attr = 0x0f; 311 con->bright_attr = true; 312 con->reverse_attr = false; 313 break; 314 case 1: // bright 315 con->bright_attr = true; 316 con->attr |= 0x08; // set the bright bit 317 break; 318 case 2: // dim 319 con->bright_attr = false; 320 con->attr &= ~0x08; // unset the bright bit 321 break; 322 case 4: // underscore we can't do 323 break; 324 case 5: // blink 325 con->attr |= 0x80; // set the blink bit 326 break; 327 case 7: // reverse 328 con->reverse_attr = true; 329 con->attr = ((con->attr & BMASK) >> 4) | ((con->attr & FMASK) << 4); 330 if (con->bright_attr) 331 con->attr |= 0x08; 332 break; 333 case 8: // hidden? 334 break; 335 336 /* foreground colors */ 337 case 30: con->attr = (con->attr & ~FMASK) | 0 | (con->bright_attr ? 0x08 : 0); break; // black 338 case 31: con->attr = (con->attr & ~FMASK) | 4 | (con->bright_attr ? 0x08 : 0); break; // red 339 case 32: con->attr = (con->attr & ~FMASK) | 2 | (con->bright_attr ? 0x08 : 0); break; // green 340 case 33: con->attr = (con->attr & ~FMASK) | 6 | (con->bright_attr ? 0x08 : 0); break; // yellow 341 case 34: con->attr = (con->attr & ~FMASK) | 1 | (con->bright_attr ? 0x08 : 0); break; // blue 342 case 35: con->attr = (con->attr & ~FMASK) | 5 | (con->bright_attr ? 0x08 : 0); break; // magenta 343 case 36: con->attr = (con->attr & ~FMASK) | 3 | (con->bright_attr ? 0x08 : 0); break; // cyan 344 case 37: con->attr = (con->attr & ~FMASK) | 7 | (con->bright_attr ? 0x08 : 0); break; // white 345 346 /* background colors */ 347 case 40: con->attr = (con->attr & ~BMASK) | (0 << 4); break; // black 348 case 41: con->attr = (con->attr & ~BMASK) | (4 << 4); break; // red 349 case 42: con->attr = (con->attr & ~BMASK) | (2 << 4); break; // green 350 case 43: con->attr = (con->attr & ~BMASK) | (6 << 4); break; // yellow 351 case 44: con->attr = (con->attr & ~BMASK) | (1 << 4); break; // blue 352 case 45: con->attr = (con->attr & ~BMASK) | (5 << 4); break; // magenta 353 case 46: con->attr = (con->attr & ~BMASK) | (3 << 4); break; // cyan 354 case 47: con->attr = (con->attr & ~BMASK) | (7 << 4); break; // white 355 } 356 } 357 } 358 359 360 static bool 361 process_vt100_command(struct console_desc *con, const char c, 362 bool seen_bracket, int32 *args, int32 argCount) 363 { 364 bool ret = true; 365 366 //dprintf("process_vt100_command: c '%c', argCount %ld, arg[0] %ld, arg[1] %ld, seen_bracket %d\n", 367 // c, argCount, args[0], args[1], seen_bracket); 368 369 if (seen_bracket) { 370 switch(c) { 371 case 'H': /* set cursor position */ 372 case 'f': { 373 int32 row = argCount > 0 ? args[0] : 1; 374 int32 col = argCount > 1 ? args[1] : 1; 375 if (row > 0) 376 row--; 377 if (col > 0) 378 col--; 379 gotoxy(con, col, row); 380 break; 381 } 382 case 'A': { /* move up */ 383 int32 deltay = argCount > 0 ? -args[0] : -1; 384 if (deltay == 0) 385 deltay = -1; 386 gotoxy(con, con->x, con->y + deltay); 387 break; 388 } 389 case 'e': 390 case 'B': { /* move down */ 391 int32 deltay = argCount > 0 ? args[0] : 1; 392 if (deltay == 0) 393 deltay = 1; 394 gotoxy(con, con->x, con->y + deltay); 395 break; 396 } 397 case 'D': { /* move left */ 398 int32 deltax = argCount > 0 ? -args[0] : -1; 399 if (deltax == 0) 400 deltax = -1; 401 gotoxy(con, con->x + deltax, con->y); 402 break; 403 } 404 case 'a': 405 case 'C': { /* move right */ 406 int32 deltax = argCount > 0 ? args[0] : 1; 407 if (deltax == 0) 408 deltax = 1; 409 gotoxy(con, con->x + deltax, con->y); 410 break; 411 } 412 case '`': 413 case 'G': { /* set X position */ 414 int32 newx = argCount > 0 ? args[0] : 1; 415 if (newx > 0) 416 newx--; 417 gotoxy(con, newx, con->y); 418 break; 419 } 420 case 'd': { /* set y position */ 421 int32 newy = argCount > 0 ? args[0] : 1; 422 if (newy > 0) 423 newy--; 424 gotoxy(con, con->x, newy); 425 break; 426 } 427 case 's': /* save current cursor */ 428 save_cur(con, false); 429 break; 430 case 'u': /* restore cursor */ 431 restore_cur(con, false); 432 break; 433 case 'r': { /* set scroll region */ 434 int32 low = argCount > 0 ? args[0] : 1; 435 int32 high = argCount > 1 ? args[1] : con->lines; 436 if (low <= high) 437 set_scroll_region(con, low - 1, high - 1); 438 break; 439 } 440 case 'L': { /* scroll virtual down at cursor */ 441 int32 lines = argCount > 0 ? args[0] : 1; 442 while (lines > 0) { 443 scrdown(con); 444 lines--; 445 } 446 break; 447 } 448 case 'M': { /* scroll virtual up at cursor */ 449 int32 lines = argCount > 0 ? args[0] : 1; 450 while (lines > 0) { 451 scrup(con); 452 lines--; 453 } 454 break; 455 } 456 case 'K': 457 if (argCount < 0) { 458 // erase to end of line 459 erase_line(con, LINE_ERASE_RIGHT); 460 } else { 461 if (args[0] == 1) 462 erase_line(con, LINE_ERASE_LEFT); 463 else if (args[0] == 2) 464 erase_line(con, LINE_ERASE_WHOLE); 465 } 466 break; 467 case 'J': 468 if (argCount < 0) { 469 // erase to end of screen 470 erase_screen(con, SCREEN_ERASE_DOWN); 471 } else { 472 if (args[0] == 1) 473 erase_screen(con, SCREEN_ERASE_UP); 474 else if (args[0] == 2) 475 erase_screen(con, SCREEN_ERASE_WHOLE); 476 } 477 break; 478 case 'm': 479 if (argCount >= 0) 480 set_vt100_attributes(con, args, argCount); 481 break; 482 default: 483 ret = false; 484 } 485 } else { 486 switch (c) { 487 case 'c': 488 reset_console(con); 489 break; 490 case 'D': 491 rlf(con); 492 break; 493 case 'M': 494 lf(con); 495 break; 496 case '7': 497 save_cur(con, true); 498 break; 499 case '8': 500 restore_cur(con, true); 501 break; 502 default: 503 ret = false; 504 } 505 } 506 507 return ret; 508 } 509 510 511 static ssize_t 512 _console_write(struct console_desc *con, const void *buf, size_t len) 513 { 514 const char *c; 515 size_t pos = 0; 516 517 while (pos < len) { 518 c = &((const char *)buf)[pos++]; 519 520 switch (con->state) { 521 case CONSOLE_STATE_NORMAL: 522 // just output the stuff 523 switch (*c) { 524 case '\n': 525 lf(con); 526 break; 527 case '\r': 528 cr(con); 529 break; 530 case 0x8: // backspace 531 del(con); 532 break; 533 case '\t': 534 tab(con); 535 break; 536 case '\0': 537 break; 538 case 0x1b: 539 // escape character 540 con->arg_ptr = -1; 541 con->state = CONSOLE_STATE_GOT_ESCAPE; 542 break; 543 default: 544 console_putch(con, *c); 545 } 546 break; 547 case CONSOLE_STATE_GOT_ESCAPE: 548 // look for either commands with no argument, or the '[' character 549 switch (*c) { 550 case '[': 551 con->state = CONSOLE_STATE_SEEN_BRACKET; 552 break; 553 default: 554 con->args[con->arg_ptr] = 0; 555 process_vt100_command(con, *c, false, con->args, con->arg_ptr + 1); 556 con->state = CONSOLE_STATE_NORMAL; 557 } 558 break; 559 case CONSOLE_STATE_SEEN_BRACKET: 560 switch (*c) { 561 case '0'...'9': 562 con->arg_ptr = 0; 563 con->args[con->arg_ptr] = *c - '0'; 564 con->state = CONSOLE_STATE_PARSING_ARG; 565 break; 566 case '?': 567 // private DEC mode parameter follows - we ignore those anyway 568 // ToDo: check if it was really used in combination with a mode command 569 break; 570 default: 571 process_vt100_command(con, *c, true, con->args, con->arg_ptr + 1); 572 con->state = CONSOLE_STATE_NORMAL; 573 } 574 break; 575 case CONSOLE_STATE_NEW_ARG: 576 switch (*c) { 577 case '0'...'9': 578 con->arg_ptr++; 579 if (con->arg_ptr == MAX_ARGS) { 580 con->state = CONSOLE_STATE_NORMAL; 581 break; 582 } 583 con->args[con->arg_ptr] = *c - '0'; 584 con->state = CONSOLE_STATE_PARSING_ARG; 585 break; 586 default: 587 process_vt100_command(con, *c, true, con->args, con->arg_ptr + 1); 588 con->state = CONSOLE_STATE_NORMAL; 589 } 590 break; 591 case CONSOLE_STATE_PARSING_ARG: 592 // parse args 593 switch (*c) { 594 case '0'...'9': 595 con->args[con->arg_ptr] *= 10; 596 con->args[con->arg_ptr] += *c - '0'; 597 break; 598 case ';': 599 con->state = CONSOLE_STATE_NEW_ARG; 600 break; 601 default: 602 process_vt100_command(con, *c, true, con->args, con->arg_ptr + 1); 603 con->state = CONSOLE_STATE_NORMAL; 604 } 605 } 606 } 607 608 return pos; 609 } 610 611 612 // #pragma mark - 613 614 615 static status_t 616 console_open(const char *name, uint32 flags, void **cookie) 617 { 618 *cookie = &gconsole; 619 620 status_t status = get_module(gconsole.module_name, (module_info **)&gconsole.module); 621 if (status == B_OK) 622 gconsole.module->clear(0x0f); 623 624 return status; 625 } 626 627 628 static status_t 629 console_freecookie(void *cookie) 630 { 631 if (gconsole.module != NULL) { 632 put_module(gconsole.module_name); 633 gconsole.module = NULL; 634 } 635 636 return B_OK; 637 } 638 639 640 static status_t 641 console_close(void *cookie) 642 { 643 // dprintf("console_close: entry\n"); 644 645 return 0; 646 } 647 648 649 static status_t 650 console_read(void *cookie, off_t pos, void *buffer, size_t *_length) 651 { 652 return B_NOT_ALLOWED; 653 } 654 655 656 static status_t 657 console_write(void *cookie, off_t pos, const void *buffer, size_t *_length) 658 { 659 struct console_desc *con = (struct console_desc *)cookie; 660 ssize_t written; 661 662 #if 0 663 { 664 const char *input = (const char *)buffer; 665 dprintf("console_write (%lu bytes): \"", *_length); 666 for (int32 i = 0; i < *_length; i++) { 667 if (input[i] < ' ') 668 dprintf("(%d:0x%x)", input[i], input[i]); 669 else 670 dprintf("%c", input[i]); 671 } 672 dprintf("\"\n"); 673 } 674 #endif 675 676 mutex_lock(&con->lock); 677 678 update_cursor(con, -1, -1); // hide it 679 written = _console_write(con, buffer, *_length); 680 update_cursor(con, con->x, con->y); 681 682 mutex_unlock(&con->lock); 683 684 if (written >= 0) { 685 *_length = written; 686 return B_OK; 687 } 688 return written; 689 } 690 691 692 static status_t 693 console_ioctl(void *cookie, uint32 op, void *buffer, size_t length) 694 { 695 return B_BAD_VALUE; 696 } 697 698 699 // #pragma mark - 700 701 702 status_t 703 init_hardware(void) 704 { 705 // iterate through the list of console modules until we find one that accepts the job 706 void *cookie = open_module_list("console"); 707 if (cookie == NULL) 708 return B_ERROR; 709 710 bool found = false; 711 712 char buffer[B_FILE_NAME_LENGTH]; 713 size_t bufferSize = sizeof(buffer); 714 715 while (read_next_module_name(cookie, buffer, &bufferSize) == B_OK) { 716 dprintf("con_init: trying module %s\n", buffer); 717 if (get_module(buffer, (module_info **)&gconsole.module) == B_OK) { 718 strlcpy(gconsole.module_name, buffer, sizeof(gconsole.module_name)); 719 put_module(buffer); 720 found = true; 721 break; 722 } 723 724 bufferSize = sizeof(buffer); 725 } 726 727 if (found) { 728 // set up the console structure 729 mutex_init(&gconsole.lock, "console lock"); 730 gconsole.module->get_size(&gconsole.columns, &gconsole.lines); 731 732 reset_console(&gconsole); 733 gotoxy(&gconsole, 0, 0); 734 save_cur(&gconsole, true); 735 } 736 737 close_module_list(cookie); 738 return found ? B_OK : B_ERROR; 739 } 740 741 742 const char ** 743 publish_devices(void) 744 { 745 static const char *devices[] = { 746 DEVICE_NAME, 747 NULL 748 }; 749 750 return devices; 751 } 752 753 754 device_hooks * 755 find_device(const char *name) 756 { 757 static device_hooks hooks = { 758 &console_open, 759 &console_close, 760 &console_freecookie, 761 &console_ioctl, 762 &console_read, 763 &console_write, 764 }; 765 766 if (!strcmp(name, DEVICE_NAME)) 767 return &hooks; 768 769 return NULL; 770 } 771 772 773 status_t 774 init_driver(void) 775 { 776 return B_OK; 777 } 778 779 780 void 781 uninit_driver(void) 782 { 783 } 784 785