Unit:
AxTerm
Description:
The TApxTerminalBuffer class defines a data structure for maintaining the data required for a communications terminal display. Essentially, this consists of:
·The characters that should be shown.
·The character sets from which those character glyphs are drawn.
·The color in which the character glyphs will be displayed.
·The color for the background behind the characters.
·A set of display attributes for the characters.
It is important to realize that the terminal buffer class is only a data structure; it has no responsibilities for the display of the data it stores. That work is delegated to the terminal and emulator components. In essence, an emulator component creates an instance of a terminal buffer to store the data that it will display on the screen. The emulator component will call methods of the terminal buffer to perform such tasks as writing characters, inserting and deleting lines, scrolling the data, changing colors and attributes, etc. These tasks will, in theory, be performed in response to control sequences being received by the terminal from some remote program or host and being decoded by an emulator. Hence, the methods of the terminal buffer class mimic the standard operations performed by all character-based terminals, but in particular by the DEC VT100 terminals.
Conceptually, the terminal buffer class manages five different screens worth of information as independent matrices. These matrices are representations of the terminal screen and hence are divided into rows and columns. One of the matrices holds the characters that should be displayed on the screen. Another matrix holds the foreground color (i.e., the color of the text), and yet another matrix holds the background color. A fourth matrix holds the character set from which the characters are drawn. The final matrix holds the special display characteristics for the text, whether it is bold, underlined, blinking and so on. Using this scheme, it is possible for every addressable character cell on the terminal display to be a different character, from different character sets, using different colors and attributes. The character matrix stores either a single byte per character cell (ANSI mode) or two bytes per character (UNICODE mode), the choice being made when the terminal buffer is created. The identifier for the character set is assumed to be limited to one of 256 possible values, and hence just a single byte is stored per character cell to hold this information. The colors are assumed to be stored as RGB values, and hence each character cell has a 4-byte long integer color value for both the foreground color and the background color. (Note that the terminal buffer does not enforce the rule that the colors have to be RGB values, you could store standard values from the 4-bit color set instead if you wishedthe terminal buffer just assumes that a color is a 4-byte quantity.) The attributes stored as a set of different possibilities are bold, underlined, strikethrough, blinking, reverse, or invisible.
A word is required here regarding character sets. The VT100 terminal is a 7-bit device. In theory, characters could have ASCII values from 0 to 127; however, the first 32 characters in this set were control characters rather than displayable characters. Hence, there were only 96 different characters that could be displayed. Since this was not sufficient, the VT100 has other sets of 96 characters that can be switched in and out, the most distinctive being the line-draw characters in the special graphics character set. To display a character at a particular position on the screen, the terminal must know both the character set from which to take the displayed glyph, and the ASCII value of the character. (A glyph is the visual representation of an ASCII value in a particular character set.) Thus, on a VT100, the ASCII value 123 would be displayed as the left curly brace, {, if the standard character set was in force, or the symbol for pi, , if the special graphics character set was in force.
Another point should to be made about the terminal buffer: it does not parse any data stream for control sequences. This is the job of the emulator portion of a terminal component. The emulator should scan the incoming data stream for control sequences (for example, those beginning with "<Esc>["), decode them, and then get the terminal buffer to update according to the operation requested.
Apart from the data to be displayed, a terminal component requires two basic pieces of information: Has the cursor moved? What needs to be redisplayed? The terminal buffer maintains a cursor position variable: the row and column number where new characters are to be written should they arrive. Various methods update the cursor position, the most obvious ones being the cursor movement methods to move the cursor up, down, left or right. Again, it should be stressed that there is no visual representation of a cursor in a terminal buffer object; the cursor is just a pair of row and column values. It is up to the terminal component displaying the data from the buffer to maintain and show a "cursor", for example, perhaps a blinking underscore or block. (By the way, this can get confusing: both the blinking point in terminals where editing takes place and the mouse pointer is called a cursor.) The terminal buffer not only maintains a cursor position, it also interfaces a routine that returns whether the cursor position has changed since the last time the routine was called.
If the terminal component is to have a chance of keeping up with fast data streams, it cannot continually redisplay the entire terminal screen every now and then. It must have a way of knowing what has changed on the terminal screen, so that it only needs to update that particular section. For example, if a character was written to the terminal, the terminal component needs to know how much of its window it needs to repaint. In the majority of cases, that's a single character cell; the new character. In some cases, the terminal data needs to be scrolled before the character can be written. In this case, the terminal display needs to repaint much more of the window to show the effect of writing the new character. The terminal buffer maintains an internal list of invalidated character cells (as cell rectangles) and the terminal component can read these and use the information to redisplay parts of its window.
The terminal buffer has two views of the data: the scrollback view and the display view. The scrollback view shows a history of data that have scrolled off the top of the display view. The display view is essentially the representation of the terminal screen itself. There are typically more rows in the scrollback view than in the display view (otherwise, there wouldn't be anything to scroll back through). The number of columns in both the scrollback and display views is however the same.
The user of the terminal buffer will refer to positions on the terminal screen as one-based values. For example, on a 24 rows x 80 columns terminal, the rows are numbered from 1 to 24 and the columns from 1 to 80. This is different from the usual way of looking at things, where values are counted from 0. The rows in the scrollback view are negative numbers or zero. For example, the last row to be scrolled off the display view (i.e., the terminal itself) will be numbered row 0, the next to last, row 1, and so on. If a 24 row terminal screen is cleared or erased, the entire scrollback view is scrolled up with the previous display, and the rows of this previous display would then be numbered 23 to 0.
The terminal buffer also supports the concept of a scrolling region. This is a region of the terminal screen to which the cursor is restricted. Once a scrolling region is defined and activated, all cursor movement is restricted to this area and characters can only be written to this area. When a scrolling region is activated, row and column numbers are no longer absolute values, counted from the top left character cell of the screen, but are now relative values, counted from the top left character cell of the scrolling region. For example, suppose a scrolling region is defined to be within rows 5 and 10 and it is activated. Moving the cursor to row 1, column 1, results in it being moved to the home position of the scrolling region, not the home position of the screen. In absolute terms, the cursor ends up at row 5, column 1 instead. The terminal buffer maintains a definition of a scrolling region and a flag that states whether the scrolling region is in force or not. If a new scrolling region is defined, the activation flag is automatically set, in other words, the scrolling region becomes effective immediately.
Generally, when a data stream is sent to a terminal, the sender does not send coloring or attribute information with every single character that needs to be displayed. Instead, the terminal is instructed to use a particular color or attribute from that point forward, until another color or attribute is requested. Characters written to the terminal in between these two instructions will automatically be in the requested color or have the requested attribute. The terminal also has a reset state, with default colors and attributes. The terminal buffer mimics this behavior by having both current values and default values for the colors, attribute, and character set. If the terminal is reset, the terminal buffer automatically sets the current values equal to the default values.