2 * Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
11 #include <KernelExport.h>
21 #define DEVICE_NAME "console"
30 CONSOLE_STATE_NORMAL
= 0,
31 CONSOLE_STATE_GOT_ESCAPE
,
32 CONSOLE_STATE_SEEN_BRACKET
,
33 CONSOLE_STATE_NEW_ARG
,
34 CONSOLE_STATE_PARSING_ARG
,
51 static struct console_desc
{
62 int32 x
; /* current x coordinate */
63 int32 y
; /* current y coordinate */
64 int32 saved_x
; /* used to save x and y */
67 int32 scroll_top
; /* top of the scroll region */
68 int32 scroll_bottom
; /* bottom of the scroll region */
75 char module_name
[B_FILE_NAME_LENGTH
];
76 console_module_info
*module
;
79 int32 api_version
= B_CUR_DRIVER_API_VERSION
;
83 update_cursor(struct console_desc
*console
, int x
, int y
)
85 console
->module
->move_cursor(x
, y
);
90 gotoxy(struct console_desc
*console
, int newX
, int newY
)
92 if (newX
>= console
->columns
)
93 newX
= console
->columns
- 1;
96 if (newY
>= console
->lines
)
97 newY
= console
->lines
- 1;
107 reset_console(struct console_desc
*console
)
109 console
->attr
= 0x0f;
110 console
->scroll_top
= 0;
111 console
->scroll_bottom
= console
->lines
- 1;
112 console
->bright_attr
= true;
113 console
->reverse_attr
= false;
117 /*! Scroll from the cursor line up to the top of the scroll region up one line.
120 scrup(struct console_desc
*console
)
122 // see if cursor is outside of scroll region
123 if (console
->y
< console
->scroll_top
|| console
->y
> console
->scroll_bottom
)
126 if (console
->y
- console
->scroll_top
> 1) {
127 // move the screen up one
128 console
->module
->blit(0, console
->scroll_top
+ 1, console
->columns
,
129 console
->y
- console
->scroll_top
, 0, console
->scroll_top
);
132 // clear the bottom line
133 console
->module
->fill_glyph(0, console
->y
, console
->columns
, 1, ' ',
138 /*! Scroll from the cursor line down to the bottom of the scroll region down
142 scrdown(struct console_desc
*console
)
144 // see if cursor is outside of scroll region
145 if (console
->y
< console
->scroll_top
|| console
->y
> console
->scroll_bottom
)
148 if (console
->scroll_bottom
- console
->y
> 1) {
149 // move the screen down one
150 console
->module
->blit(0, console
->y
, console
->columns
,
151 console
->scroll_bottom
- console
->y
, 0, console
->y
+ 1);
154 // clear the top line
155 console
->module
->fill_glyph(0, console
->y
, console
->columns
, 1, ' ',
161 lf(struct console_desc
*console
)
163 //dprintf("lf: y %d x %d scroll_top %d scoll_bottom %d\n", console->y, console->x, console->scroll_top, console->scroll_bottom);
165 if (console
->y
== console
->scroll_bottom
) {
166 // we hit the bottom of our scroll region
168 } else if(console
->y
< console
->scroll_bottom
) {
175 rlf(struct console_desc
*console
)
177 if (console
->y
== console
->scroll_top
) {
178 // we hit the top of our scroll region
180 } else if (console
->y
> console
->scroll_top
) {
187 cr(struct console_desc
*console
)
194 del(struct console_desc
*console
)
196 if (console
->x
> 0) {
198 } else if (console
->y
> 0) {
200 console
->x
= console
->columns
- 1;
202 //This doesn't work...
205 //console->x = console->columns - 1;
208 console
->module
->put_glyph(console
->x
, console
->y
, ' ', console
->attr
);
213 erase_line(struct console_desc
*console
, erase_line_mode mode
)
216 case LINE_ERASE_WHOLE
:
217 console
->module
->fill_glyph(0, console
->y
, console
->columns
, 1, ' ',
220 case LINE_ERASE_LEFT
:
221 console
->module
->fill_glyph(0, console
->y
, console
->x
+ 1, 1, ' ',
224 case LINE_ERASE_RIGHT
:
225 console
->module
->fill_glyph(console
->x
, console
->y
,
226 console
->columns
- console
->x
, 1, ' ', console
->attr
);
235 erase_screen(struct console_desc
*console
, erase_screen_mode mode
)
238 case SCREEN_ERASE_WHOLE
:
239 console
->module
->clear(console
->attr
);
241 case SCREEN_ERASE_UP
:
242 console
->module
->fill_glyph(0, 0, console
->columns
, console
->y
+ 1,
245 case SCREEN_ERASE_DOWN
:
246 console
->module
->fill_glyph(console
->y
, 0, console
->columns
,
247 console
->lines
- console
->y
, ' ', console
->attr
);
256 save_cur(struct console_desc
*console
, bool saveAttrs
)
258 console
->saved_x
= console
->x
;
259 console
->saved_y
= console
->y
;
262 console
->saved_attr
= console
->attr
;
267 restore_cur(struct console_desc
*console
, bool restoreAttrs
)
269 console
->x
= console
->saved_x
;
270 console
->y
= console
->saved_y
;
273 console
->attr
= console
->saved_attr
;
278 console_putch(struct console_desc
*console
, const char c
)
280 if (++console
->x
>= console
->columns
) {
284 console
->module
->put_glyph(console
->x
- 1, console
->y
, c
, console
->attr
);
290 tab(struct console_desc
*console
)
292 console
->x
= (console
->x
+ TAB_SIZE
) & ~TAB_MASK
;
293 if (console
->x
>= console
->columns
) {
294 console
->x
-= console
->columns
;
301 set_scroll_region(struct console_desc
*console
, int top
, int bottom
)
305 if (bottom
>= console
->lines
)
306 bottom
= console
->lines
- 1;
310 console
->scroll_top
= top
;
311 console
->scroll_bottom
= bottom
;
316 set_vt100_attributes(struct console_desc
*console
, int32
*args
, int32 argCount
)
319 // that's the default (attributes off)
324 for (int32 i
= 0; i
< argCount
; i
++) {
325 //dprintf("set_vt100_attributes: %ld\n", args[i]);
328 console
->attr
= 0x0f;
329 console
->bright_attr
= true;
330 console
->reverse_attr
= false;
333 console
->bright_attr
= true;
334 console
->attr
|= 0x08; // set the bright bit
337 console
->bright_attr
= false;
338 console
->attr
&= ~0x08; // unset the bright bit
340 case 4: // underscore we can't do
343 console
->attr
|= 0x80; // set the blink bit
346 console
->reverse_attr
= true;
347 console
->attr
= ((console
->attr
& BMASK
) >> 4)
348 | ((console
->attr
& FMASK
) << 4);
349 if (console
->bright_attr
)
350 console
->attr
|= 0x08;
355 /* foreground colors */
356 case 30: console
->attr
= (console
->attr
& ~FMASK
) | 0 | (console
->bright_attr
? 0x08 : 0); break; // black
357 case 31: console
->attr
= (console
->attr
& ~FMASK
) | 4 | (console
->bright_attr
? 0x08 : 0); break; // red
358 case 32: console
->attr
= (console
->attr
& ~FMASK
) | 2 | (console
->bright_attr
? 0x08 : 0); break; // green
359 case 33: console
->attr
= (console
->attr
& ~FMASK
) | 6 | (console
->bright_attr
? 0x08 : 0); break; // yellow
360 case 34: console
->attr
= (console
->attr
& ~FMASK
) | 1 | (console
->bright_attr
? 0x08 : 0); break; // blue
361 case 35: console
->attr
= (console
->attr
& ~FMASK
) | 5 | (console
->bright_attr
? 0x08 : 0); break; // magenta
362 case 36: console
->attr
= (console
->attr
& ~FMASK
) | 3 | (console
->bright_attr
? 0x08 : 0); break; // cyan
363 case 37: console
->attr
= (console
->attr
& ~FMASK
) | 7 | (console
->bright_attr
? 0x08 : 0); break; // white
365 /* background colors */
366 case 40: console
->attr
= (console
->attr
& ~BMASK
) | (0 << 4); break; // black
367 case 41: console
->attr
= (console
->attr
& ~BMASK
) | (4 << 4); break; // red
368 case 42: console
->attr
= (console
->attr
& ~BMASK
) | (2 << 4); break; // green
369 case 43: console
->attr
= (console
->attr
& ~BMASK
) | (6 << 4); break; // yellow
370 case 44: console
->attr
= (console
->attr
& ~BMASK
) | (1 << 4); break; // blue
371 case 45: console
->attr
= (console
->attr
& ~BMASK
) | (5 << 4); break; // magenta
372 case 46: console
->attr
= (console
->attr
& ~BMASK
) | (3 << 4); break; // cyan
373 case 47: console
->attr
= (console
->attr
& ~BMASK
) | (7 << 4); break; // white
380 process_vt100_command(struct console_desc
*console
, const char c
,
381 bool seenBracket
, int32
*args
, int32 argCount
)
385 //dprintf("process_vt100_command: c '%c', argCount %ld, arg[0] %ld, arg[1] %ld, seenBracket %d\n",
386 // c, argCount, args[0], args[1], seenBracket);
390 case 'H': // set cursor position
393 int32 row
= argCount
> 0 ? args
[0] : 1;
394 int32 col
= argCount
> 1 ? args
[1] : 1;
399 gotoxy(console
, col
, row
);
404 int32 deltaY
= argCount
> 0 ? -args
[0] : -1;
407 gotoxy(console
, console
->x
, console
->y
+ deltaY
);
411 case 'B': // move down
413 int32 deltaY
= argCount
> 0 ? args
[0] : 1;
416 gotoxy(console
, console
->x
, console
->y
+ deltaY
);
419 case 'D': // move left
421 int32 deltaX
= argCount
> 0 ? -args
[0] : -1;
424 gotoxy(console
, console
->x
+ deltaX
, console
->y
);
428 case 'C': // move right
430 int32 deltaX
= argCount
> 0 ? args
[0] : 1;
433 gotoxy(console
, console
->x
+ deltaX
, console
->y
);
437 case 'G': // set X position
439 int32 newX
= argCount
> 0 ? args
[0] : 1;
442 gotoxy(console
, newX
, console
->y
);
445 case 'd': // set y position
447 int32 newY
= argCount
> 0 ? args
[0] : 1;
450 gotoxy(console
, console
->x
, newY
);
453 case 's': // save current cursor
454 save_cur(console
, false);
456 case 'u': // restore cursor
457 restore_cur(console
, false);
459 case 'r': // set scroll region
461 int32 low
= argCount
> 0 ? args
[0] : 1;
462 int32 high
= argCount
> 1 ? args
[1] : console
->lines
;
464 set_scroll_region(console
, low
- 1, high
- 1);
467 case 'L': // scroll virtual down at cursor
469 int32 lines
= argCount
> 0 ? args
[0] : 1;
476 case 'M': // scroll virtual up at cursor
478 int32 lines
= argCount
> 0 ? args
[0] : 1;
486 if (argCount
== 0 || args
[0] == 0) {
487 // erase to end of line
488 erase_line(console
, LINE_ERASE_RIGHT
);
489 } else if (argCount
> 0) {
491 erase_line(console
, LINE_ERASE_LEFT
);
492 else if (args
[0] == 2)
493 erase_line(console
, LINE_ERASE_WHOLE
);
497 if (argCount
== 0 || args
[0] == 0) {
498 // erase to end of screen
499 erase_screen(console
, SCREEN_ERASE_DOWN
);
502 erase_screen(console
, SCREEN_ERASE_UP
);
503 else if (args
[0] == 2)
504 erase_screen(console
, SCREEN_ERASE_WHOLE
);
509 set_vt100_attributes(console
, args
, argCount
);
517 reset_console(console
);
526 save_cur(console
, true);
529 restore_cur(console
, true);
541 _console_write(struct console_desc
*console
, const void *buffer
, size_t length
)
546 while (pos
< length
) {
547 c
= &((const char *)buffer
)[pos
++];
549 switch (console
->state
) {
550 case CONSOLE_STATE_NORMAL
:
551 // just output the stuff
559 case 0x8: // backspace
573 console
->arg_count
= 0;
574 console
->state
= CONSOLE_STATE_GOT_ESCAPE
;
577 console_putch(console
, *c
);
580 case CONSOLE_STATE_GOT_ESCAPE
:
581 // Look for either commands with no argument, or the '['
585 console
->state
= CONSOLE_STATE_SEEN_BRACKET
;
588 console
->args
[0] = 0;
589 process_vt100_command(console
, *c
, false, console
->args
,
591 console
->state
= CONSOLE_STATE_NORMAL
;
595 case CONSOLE_STATE_SEEN_BRACKET
:
598 console
->arg_count
= 0;
599 console
->args
[console
->arg_count
] = *c
- '0';
600 console
->state
= CONSOLE_STATE_PARSING_ARG
;
603 // Private DEC mode parameter follows - we ignore those
605 // TODO: check if it was really used in combination with
609 process_vt100_command(console
, *c
, true, console
->args
,
611 console
->state
= CONSOLE_STATE_NORMAL
;
615 case CONSOLE_STATE_NEW_ARG
:
618 console
->arg_count
++;
619 if (console
->arg_count
== MAX_ARGS
) {
620 console
->state
= CONSOLE_STATE_NORMAL
;
623 console
->args
[console
->arg_count
] = *c
- '0';
624 console
->state
= CONSOLE_STATE_PARSING_ARG
;
627 process_vt100_command(console
, *c
, true, console
->args
,
628 console
->arg_count
+ 1);
629 console
->state
= CONSOLE_STATE_NORMAL
;
633 case CONSOLE_STATE_PARSING_ARG
:
637 console
->args
[console
->arg_count
] *= 10;
638 console
->args
[console
->arg_count
] += *c
- '0';
641 console
->state
= CONSOLE_STATE_NEW_ARG
;
644 process_vt100_command(console
, *c
, true, console
->args
,
645 console
->arg_count
+ 1);
646 console
->state
= CONSOLE_STATE_NORMAL
;
660 console_open(const char *name
, uint32 flags
, void **cookie
)
664 status_t status
= get_module(sConsole
.module_name
, (module_info
**)&sConsole
.module
);
666 sConsole
.module
->clear(0x0f);
673 console_freecookie(void *cookie
)
675 if (sConsole
.module
!= NULL
) {
676 put_module(sConsole
.module_name
);
677 sConsole
.module
= NULL
;
685 console_close(void *cookie
)
687 // dprintf("console_close: entry\n");
694 console_read(void *cookie
, off_t pos
, void *buffer
, size_t *_length
)
696 return B_NOT_ALLOWED
;
701 console_write(void *cookie
, off_t pos
, const void *buffer
, size_t *_length
)
703 struct console_desc
*console
= (struct console_desc
*)cookie
;
708 const char *input
= (const char *)buffer
;
709 dprintf("console_write (%lu bytes): \"", *_length
);
710 for (uint32 i
= 0; i
< *_length
; i
++) {
712 dprintf("(%d:0x%x)", input
[i
], input
[i
]);
714 dprintf("%c", input
[i
]);
720 mutex_lock(&console
->lock
);
722 update_cursor(console
, -1, -1); // hide it
723 written
= _console_write(console
, buffer
, *_length
);
724 update_cursor(console
, console
->x
, console
->y
);
726 mutex_unlock(&console
->lock
);
737 console_ioctl(void *cookie
, uint32 op
, void *buffer
, size_t length
)
739 struct console_desc
*console
= (struct console_desc
*)cookie
;
741 if (op
== TIOCGWINSZ
) {
743 size
.ws_xpixel
= size
.ws_col
= console
->columns
;
744 size
.ws_ypixel
= size
.ws_row
= console
->lines
;
746 return user_memcpy(buffer
, &size
, sizeof(struct winsize
));
759 // iterate through the list of console modules until we find one that accepts the job
760 void *cookie
= open_module_list("console");
766 char buffer
[B_FILE_NAME_LENGTH
];
767 size_t bufferSize
= sizeof(buffer
);
769 while (read_next_module_name(cookie
, buffer
, &bufferSize
) == B_OK
) {
770 dprintf("con_init: trying module %s\n", buffer
);
771 if (get_module(buffer
, (module_info
**)&sConsole
.module
) == B_OK
) {
772 strlcpy(sConsole
.module_name
, buffer
, sizeof(sConsole
.module_name
));
778 bufferSize
= sizeof(buffer
);
782 // set up the console structure
783 mutex_init(&sConsole
.lock
, "console lock");
784 sConsole
.module
->get_size(&sConsole
.columns
, &sConsole
.lines
);
786 reset_console(&sConsole
);
787 gotoxy(&sConsole
, 0, 0);
788 save_cur(&sConsole
, true);
791 close_module_list(cookie
);
792 return found
? B_OK
: B_ERROR
;
797 publish_devices(void)
799 static const char *devices
[] = {
809 find_device(const char *name
)
811 static device_hooks hooks
= {
818 /* Leave select/deselect/readv/writev undefined. The kernel will
819 * use its own default implementation. The basic hooks above this
820 * line MUST be defined, however. */
827 if (!strcmp(name
, DEVICE_NAME
))