by Steffen
Seeger
last updated $Date: 1997/12/14 21:55:31 $
This document should give an overview
of the console code, explain the scroller programming
interface and give a hint what needs to be rewritten
when implementing a new terminal parser.
The console code, which is implemented in file:linux/drivers/char/console.c implements the neccessary functions needed to handle a text display and scrolling on it. It does so by extending the struct kgi_device by the neccessary fields to keep the console state in a struct kgi_console. This is done in file:ggi/include/console.h and header files included from there. With the current implementation, there is a drawback in terms of binary compatibilty with the terminal drivers which is one reason why they aren't loadable yet. Currently the kernel needs to know about all terminal drivers that are possible at compile time. So, adding a new terminal emulation driver needs currently a kernel recompile. This will probably change in the future, but the basic interface will remain as it is now, as long as we don't face major problems.
The console code handles all the neccessary interfacing to the TTY layer and the display hardware and is optimized for several levels of hardware access which are currently
This implementation has been written from scratch and performs - at least for the most common case of normal text output better than the old Linux-2.0 code - from 30% to 50% better in comparable hardware configurations. We expect a significant speed up when the code interfacing to the TTY layer can be tuned to allow 'burst' writes to the parsers, which are prepared for this. If you know a solution here, your expertise is welcome.
The scroller is a simple abstraction from terminals that provides the basic functions to build terminal parsers easily. A terminal parser should use these functions to do it's output and not touch the framebuffers directly. This is because the scroller is highly optimized to produce fast output and transparent backing store even on slow and limited hardware. It provides the following features for terminal drivers:
All scroller function names have the prefix "scroll_" and are implemented in file:linux/drivers/char/console.c. Some of them interface to other parts of KGI and for terminal drivers you will never have to worry about them. Those that might be of interest for you are describied below in more detail. If a function refers to pixels, the number of the pixel is used, not the coordinates. The pixel number of a pixel at (x,y) is y*FB_SIZE + x where FB_SIZEX is the stride of the frame buffer.
void scroll_modified(struct kgi_console *cons,ggi_uint from,ggi_uint to);
This function marks the area from pixel number from to pixel number to to be 'dirty' meaning they will be syncronized next time scroll_sync() is called. from must not be greater than to and cons must be valid. From a series of calls to scroll_sync(), the pixels with numbers from including the minimal from value to including the maximal to value are marked for update.
void scroll_update_attr(struct kgi_console *cons);
Update the internal attribute pixel value 'cache' to match the full set attributes. Which attributes are supported depends on the display and the translation from the generic attribute into those visible on the display is subject to the display driver. So, the same attributes may look different on several display or may be silently ignored.
void scroll_update_gadgets(struct kgi_console *cons);
Update the gadgets on the screen to match the internal state. This does show the pointer and cursor mark at the positions given in the keyboard state. To make the cursor or pointer mark appear on the screen, you must set the corresponding flags in DEV.state which are KGI_POINTER_TO_SHOW and KGI_CURSOR_TO_SHOW.
void scroll_sync(struct kgi_console *cons);
Perform the neccessary actions to synchronize the current internal buffer state with the image displayed. This may do nothing or start complex drawing operations, depending on the capabilities of the display hardware. This may be very time consuming, so it should be called only when the internal state is not going to change again.
void scroll_write(struct kgi_console *cons, unicode c);
Put the font representation of the character c at the current writing position and move the writing position one position to the right. This does not mark the modified area using scroll_modified(), because usually you have to write lots of characters and don't change the writing position against the normal direction. Thus the intended use is to keep the current writing position POS before a series of scroll_write() and call scroll_modified(oldpos, POS); whenever moving the current writing direction against the normal writing direction (which is left to right, top to down). Also, scroll_write() does not perform a CR LF sequence or change the writing position when the current writing position reaches the leftmost column. Instead it sets the flag need_wrap which can be checked using FLAG(need_wrap) by the parser.
void scroll_lf(struct kgi_console *cons);
Perform a line feed operation, that is move the writing position down one line or scroll up line if at the bottom line of the current scrolling region. This marks the modified areas properly using scroll_modified().
void scroll_reverse_lf(struct kgi_console *cons);
Perform a reverse linefeed operation, that is move the writing position up one line or scroll up if at or above the top line of the current scrolling region. This marks the modified areas properly using scroll_modified().
void scroll_gotoxy(struct kgi_console *cons, int new_x, int new_y);
Move the current writing postion to the given position at the screen. You should mark the modified area before and refetch the current writing position afterwards.
void scroll_cr(struct kgi_console *cons);
Perform a carriage return, that is put the writing position at the beginning of the current line. This does mark modifications you made, so you should flush any pending modifications you made using scroll_modified() before calling scroll_cr(). Fetch the new value of POS as the pixel number where scroll_write() will put characters to.
void scroll_bs(struct kgi_console *cons);
Perform a backspace, that is move the current writing postion backward one position. No action is taken if the current writing position is at the leftmost column. Also, scroll_bs() does not care about the modified area, thus you should call scroll_modified() before and fetch the current writing position after a call to scroll_bs().
void scroll_need_sync(struct kgi_console *cons);
This marks the given console to need an sync operation. This function should be used from the handle_event function, because a possibly time consuming scroll_sync() operation is not allowed in there.
void scroll_mksound(struct kgi_console *cons, int pitch, int duration);
This produces a sound of the given pitch in Hz for the given duration in ms.
void scroll_up(struct kgi_console *cons, ggi_uint t, ggi_uint b, ggi_uint n);
Scroll the area between top and bottom up by n lines and clear the 'fresh' area using the ERASE attributes. If the parameters are invalid, that is bottom is less or equal top or bottom is more than SIZE_Y, or n is zero; no action is taken. scroll_up() marks the area modifed using scroll_modified().
void scroll_down(struct kgi_console *cons, ggi_uint t, ggi_uint b, ggi_uint n);
Scroll the area between top and bottom down by n lines and clear the 'fresh' area using the ERASE attributes. If the parameters are invalid, that is bottom is less or equal top or bottom is more than SIZE_Y, or n is zero; no action is taken. scroll_up() marks the area modifed using scroll_modified().
void scroll_erase_display(struct kgi_console *cons, int arg);
Clear parts of the screen using the ERASE attributes. Depending on arg the following areas are cleared:
arg | action |
0 | clear from including the current writing position to including the end of screen |
1 | clear from including start of the screen to including the current writing position |
2 | clear the whole screen |
scroll_erase_display() marks the modified areas properly using scroll_modified().
void scroll_erase_line(struct kgi_console *cons, int arg);
Perform erase in line operations depending on arg:
arg | action |
0 | clear from including current writing position to including end of line |
1 | clear from including start of line to including the current writing position |
2 | clear the whole line |
scroll_erase_line() marks the modified area properly unsing scroll_modified().
void scroll_insert_chars(struct kgi_console *cons, ggi_uint n);
This inserts n characters at the current writing position in the current line. The 'fresh' area is cleared using the ERASE attributes. The modified area is marked properly using scroll_modified().
void scroll_delete_chars(struct kgi_console *cons, ggi_uint n);
This deletes n characters at the current writing position in the current line. The 'fresh' area is cleared using the ERASE attributes. The modified area is marked properly using scroll_modified().
void scroll_erase_chars(struct kgi_console *cons, ggi_uint n);
This erases (overwrites) n characters starting at the current writing position in the current line. The erased area is cleared using the ERASE attributes. The modified area is marked properly using scroll_modified().
void scroll_backward(struct kgi_console *cons, int lines);
Increase the scrollback-offset by half the screen size (lines <= 0) or the specified amount of lines (lines > 0). This also marks the given console to need a scroll_sync() operation.
void scroll_forward(struct kgi_console *cons, int lines);
Decrease the scrollback-offset by half the screen size (lines <= 0) or the specified amount of lines (lines > 0). This also marks the given console to need a scroll_sync() operation.
However, there are some pitfalls you may step into when starting a new terminal driver:
All needed to extend GGI by another terminal parser is basically the implementation of the following routines (Note that we reserve to implement parser specific ioctl()-calls for later versions):
The purpose of each of the routines is explained in more detail below, it is suggested you have the neccessary include files at hand to look up the data structures. A more complicated implementation can be found in file:linux/drivers/char/terminals/xterm.c
This function is called from an interrupt routine and should run as short as possible. It might be called from an IRQ bottom half handler in future releases, but you should the neccessary things as fast as possible here. Let' s have a look at the version for the dumb parser implemented in file:linux/drivers/char/console.c:
static void dumb_handle_event(struct kgi_device *dev, ggi_event
*ev)
{
#define cons ((struct kgi_console *) dev) /* dev is guaranteed to
be a console */
int sym = ev->kbd.keysym;
int type = KTYP(sym);
int val = KVAL(sym);
ASSERT(dev->type == KGI_CONSOLE_DEV);
if ( !((ev->type == evKeyPress) || (ev->type == evKeyRepeat)) || (sym == K_HOLE) || !TTY) {
return;
}
if (type >= 0xF0) {
type -= 0xF0;
if ((type == KT_LETTER) || (type == KT_LATIN)) {
tty_insert_flip_char(TTY, val, 0);
}
if (type == KT_SPEC) {
switch (K(KT_SPEC,val)) {
case K_HOLD:
if (TTY->stopped) {
start_tty(TTY);
} else {
stop_tty(TTY);
}
return;
case K_ENTER:
tty_insert_flip_char(TTY, ASCII_CR, 0);
break;
case K_SCROLLFORW:
scroll_forward(cons, 0); break;
case K_SCROLLBACK:
scroll_backward(cons, 0); break;
default:
return;
}
}
wake_up(&keypress_wait);
tty_schedule_flip(TTY);
}
#undef cons
}
As you can see, this basically puts the characters to report to the application(s) into the TTYs flip-buffer and marks it for rescheduling. Which events are reported to handle_event() is determined by dev->event_mask which can be set as the device wants it. So it is possible to recieve keypress events, pointer moves and button presses etc. or even scancodes or keycodes. Thus one could write an emulation of the Linux-2.0 terminal code very simple. (It should be noted here that in order to emulate the behavior of the Linux-2.0 code in terms of console switches, the scroll_save() and scroll_restore() functions will need some adjustment. Currently there are higher priorites for us, which is why this isn't done yet.)
This function should reset the parser specific state in the struct kgi_console argument passed by reference. The calling interface to this function might change slightly in the future, but the purpose will remain. To give you a clue about the basic tasks this has too perform, again the implementation for the dumb console parser taken from file:linux/drivers/char/console.c:
static void dumb_do_reset(struct kgi_console *cons, ggi_uint do_reset)
{
DEV.event_mask = emKeyPress | emKeyRepeat;
if (DEV.state & KGI_MAPPED_ON_DISPLAY) {
kgi_undo_gadgets(&DEV);
}
cons->scroll.mode = 0;
SET_MODE(show_cursor);
SET_MODE(auto_wrap);
if (TTY) {
TTY->winsize.ws_col = SIZE_X;
TTY->winsize.ws_row = SIZE_Y;
}
ORIGIN = ORG = 0;
TOP = 0;
BOTTOM = SIZE_Y;
BELL_PITCH = 440 /* Hz */;
BELL_DURATION = 200 /* msec */;
ATTRFL = ATTR_NORMAL | ATTR_COLOR(COL_LIGHTGRAY, COL_BLACK);
scroll_update_attr(cons);
scroll_gotoxy(cons, 0, 0);
scroll_erase_display(cons, 2);
scroll_update_gadgets(cons);
scroll_sync(cons);
kbd_reset(&(KBD_STATE));
if (DEV.state & KGI_MAPPED_ON_DISPLAY) {
kgi_show_gadgets(&(DEV));
}
/* this needs to handle the keyboard leds too, but this isn't implemented yet. */
}
The do_reset() function is called from the console code after allocation and basic initialisation of the struct kgi_console associated with a TTY special file when one of these (/dev/tty??) is opened. [NOTE: This documentation is under construction, expect an update here!]