# The grid

The grid is a matrix of glyphs that represents what is seen on a screen. It has
a fixed number of columns (COLS) and lines (LINES) and the glyphs is contains
are adressable by a linear position encoded as "y*COLS+x". For example, if a
grid has 80 columns, position 84 is the 4th glyph, from the left, of the 2nd
line.

The grid API revolves around the "grid" value, around which all its words are
built. One creates a new grid through "newgrid" (or rather a grid implementation
"new" word) which yield a pointer to a struct compatible with the "grid" value.

The Grid API allows one to write a character at an arbitrary position in the
matrix, through for example "cell! ( c pos -- )".

The grid is buffered. It maintains a buffer of COLS*LINES characters that
represents what should normally be on screen considering past "cell!" calls.
This buffer allows scrolling without the backend having to read from video
memory, which is often much slower than writing to it. Moreover, "cell!" doesn't
call the backend method if "c" is the same as what is currently in buffer. This
makes scrolling much faster for the empty area of the screen.

## Colors

Each cell in the grid can have a background and a foreground color associated to
it. This information is encoded as a pair of 4-bit nibbles, which make each cell
in the matrix use 16-bit total.

This means that we have 16 colors, which is organized as a palette. Referring to
colors by their index is not very evocative, so this unit supplies constants
that associate numbers 0 to 15 to the historical VGA text mode palette colors:

0  BLACK    8  DGRAY
1  BLUE     9  LBLUE
2  GREEN    10 LGREEN
3  CYAN     11 LCYAN
4  RED      12 LRED
5  MAGENTA  13 LMAGENTA
6  BROWN    14 YELLOW
7  LGRAY    15 WHITE

That color is passed as b15:8 (b15:12 for background, b11:8 for foreground) of
the "g" argument of "rawcell!". The grid backend then do whatever it wants with
that information.

The "colors" variable determine what that b15:8 part of "g" will be in the next
"cell!" call. Moreover, we have the "normalcolors" and "highlightcolors"
variables that contain the value that "colors" should contain in "normal" mode
and in "highlighted" mode. It is expected of the grid backend to set those
values to something sensible on initialization.

This way, applications can use "normal" and "highlight" and expect a behavior
that is consistent across grid backends.

## Emitting

This unit supplies "gridrtype ( a u -- )", a word that can be plugged in RTYPE
and that uses the grid as a console. For this purpose, it has to maintain a
"current position", which can be set with "at-xy ( x y -- )"

If a NL character ($0a) is emitted, the grid skips to the next line. If a BS
character ($08) is written to it, the position goes back one character and a
SPC character is written over previous position.

In newline conditions (NL character or wrapping around the end of line), we
clear the next line and move the position there. If we're at the end of the
screen, we scroll if the underlying driver allows it. Otherwise, we wrap around
to the top.

## Cursor

The grid also includes the concept of "cursor", which is a visual position
indicator. Its position is not related to what we call "emit position" above.
It is explicitly updated through "cursor! ( pos -- )".

Implementation of the visual cue for the cursor is machine dependent. For
example, [drv/pc/vga] has its own concept of cursor which we use. However. this
unit also supplies its own default cursor implementation under "defcursor!".

What you'll typically want to do is to hook cursor updates to "waitkey" in
[io/prompt].

## Typical initialization

  needs io/grid io/prompt io/mygridimpl
  newmygrid to grid
  ' gridrtype RTYPE !
  :realias waitkey pos cursor! key ;

## API

### Constants

BLACK WHITE BLUE RED ... See "Colors" above.

### Values

COLS
  Width, in characters, of the grid.

LINES
  Height, in characters, of the grid.

colors
  Colors that will be attributed to the next "cell!" calls.

normalcolors
  Value that "colors" should have in "normal" mode.

highlightcolors
  Value that "colors" should have in "highlight" mode.

buffer
  Pointer to the grid buffer, a COLS*LINES array of 16-bit color/character pairs

### Methods

rawcell! ( g pos -- )
  Set content of the cell at "pos" to glyph "g", bypassing buffering. A glyph is
  a 16-bit number with the lower part being the character and b15:8 being the
  color.

cursor! ( pos -- )
  Move cursor to "pos".

newln ( oldln -- newln )
  Prepare new line under line number "newln" and return the line number of that
  prepared line as "newln". Normally, "newln" is "oldln+1", but if scrolling is
  involved, then "newln" will be "oldln".

### Words

grid$ ( 'newln 'cursor! 'rawcell! cols lines -- )
  Setup the grid with the specifield fields. The three first one are pointers
  to words implementing the words with the corresponding name, described above.

cell! ( c pos -- )
  Set content of the cell at "pos" to character "c". This is a front for
  "rawcell!". Before being sent to "rawcell!", the "colors" field is combined
  with "c" to form a glyph and the buffer is looked up. If the value is the
  same, "rawcell!" is not called. Otherwise, it is called and the buffer is
  updated.

normal ( -- )
  Set "colors" to "normalcolors"

highlight ( -- )
  Set "colors" to "highlightcolors"

?highlight ( f -- )
  Set "colors" to "normalcolors" if f=0, otherwise "highlightcolors".

defcursor! ( pos -- )
  A default implementation of "cursor!" where we look into the grid buffer at
  its old position, re-write the same character with "highlight" off, then look
  in the buffer at "pos" and re-write the same character with "highlight" on.

at-xy ( x y -- )
  Set "emit position" to coordinates corresponding to "x y". Cursor not
  affected.

poscursor! ( pos -- )
  Set current pos to "pos" and update cursor position.

poscnt ( -- n )
  Yields COLS * LINES.

clrline ( ln -- )
  Set all cells in line number "ln" to spaces.

clear ( -- )
  Clear the whole screen as well as the internal grid buffer. This can be used
  after the display has been filled with something outside of the grid's control
  to avoid having the buffer create glitches on the screen.

scroll ( -- )
  Read the buffer starting from the second line and for each character, call
  "cell!" for a position that is one line higher.

dbgnum1! ( n -- )
  Spit "n", formatted with ".x", at the top right corner of the screen. Useful
  for debugging purposes. To debug live numbers, you can combine this with
  lib/loop.

dbgnum2! ( n -- )
  Same as dbgnum1!, but one slot to the left.

showruler ( -- )
  Fill upper and right edges of the grid in order to easily see its size.

showcolors ( -- )
  Emit 'X' 256 times, with all possible color combinations.
