.\" pinfo_ti.man
.\"
.\"  ``pinfocom'' -- a portable Infocom Inc. data file interpreter.
.\"  Copyright (C) 1987-1992  InfoTaskForce
.\"
.\"  This program is free software; you can redistribute it and/or modify
.\"  it under the terms of the GNU General Public License as published by
.\"  the Free Software Foundation; either version 2 of the License, or
.\"  (at your option) any later version.
.\"
.\"  This program is distributed in the hope that it will be useful,
.\"  but WITHOUT ANY WARRANTY; without even the implied warranty of
.\"  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
.\"  GNU General Public License for more details.
.\"
.\"  You should have received a copy of the GNU General Public License
.\"  along with this program; see the file COPYING.  If not, write to the
.\"  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
.\"
.\" $Header: RCS/pinfo_ti.n,v 3.0 1992/10/21 16:58:31 pds Stab $
.\"
.TH PINFO_TI 3 "21 October 1992" "Revision 3.0" "Contributed Software"
.SH NAME
.B pinfo_ti
\- Description of the terminal interface to the portable Infocom
datafile interpreter.
.SH SYNOPSIS
.nf
.ft B
#include <stdio.h>
#include "infocom.h"

extern gflags_t gflags;
extern char     *ti_location;
extern char     *ti_status;


const char *scr_usage;
const char *scr_long_usage;
const char *scr_opt_list;

int scr_cmdarg( int argc, char ***argv_p );

int scr_getopt( int c, const char *arg );

void scr_setup( int margin,
                int indent,
                int lines,
                int context );

void scr_begin();

void scr_putline( const char *buffer );

void scr_putscore();

void scr_putsound( int number,
                   int action,
                   int volume,
                   int argc );

void scr_putmesg( const char *buffer, Bool is_error );

int scr_getline( const char *prompt,
                 int length,
                 char *buffer );

void scr_window( int size );

void scr_set_win( int win );

FILE *scr_open_sf( int length, char *buffer, int type );

void scr_close_sf( const char *buffer, FILE *fp, int is_save );

void scr_end();

void scr_shutdown();
.ft R
.fi
.SH DESCRIPTION
These routines form the interface between the portable Infocom
datafile interpreter (pinfocom) and a particular type of terminal.
The pinfocom package comes with interfaces to the following terminal
types:
.TP
.B termcap
This terminal type supports all(?) UNIX systems via an interface to
either termcap or terminfo terminal capability and either termio,
termios, or sgtty line discipline control.  Optional support for the
GNU readline line editing and history library is also available. 
.TP
.B amiga
This terminal type supports an Amiga interface, including sound and
command line editing.  See amiga.c for full details. 
.TP
.B msdos
This terminal type supports MS-DOS based systems.  See msdos.c for
full details. 
.TP
.B stream
This terminal type should support any system with a C compiler; it
uses simple C stdio routines that all C libraries will have.  Nothing
fancy, but it works. 
.P
Also in the works is an X11-based interface. 
.P
This man page describes the interface between the pinfo interpreter
and the terminal support packages so that programmers can create their
own interfaces to new systems.
.I "Terminal interface"
will be abbreviated to
.B TI
below. 
.SS Environment
The interpreter operates in two modes: game-playing mode
(default) and information mode (if any of the command-line
options 
.BR \-h ,
.BR \-o ,
.BR \-O ,
.BR \-v
and/or
.B \-V
are given), where the game is not played but information about it is
instead displayed on the screen. 
.P
All input and output in game-playing mode is done via the
.B scr_*()
functions described below.  Output during information mode is via
simple C stdio library functions. 
.P
The only interface between the interpreter and the terminal handling
code is through the functions and global variables described below;
no other symbols in the TI code should be externally
visible and any other external symbols in the interpreter code are
subject to change. 
.SH "GENERAL"
Some general issues to be considered:
.SS "Asynchronous Events"
The interpreter registers a signal handler for the SIGINT signal and
sets to ignore the SIGQUIT signal (if defined). 
.P
Any other signals or other asynchronous events are free to be handled
by the TI as it sees fit.  Note, however, that none of these handler
routines may modify or access any interpreter variables: interpreter
variables listed below are only guaranteed to be valid when one of the
scr_*() functions below has been properly called from within the
interpreter code. 
.SS "Proportional Fonts"
If the TI supports multiple fonts it is perfectly reasonable to use
proportional-width fonts for printing general game text if you wish. 
.P
However, at certain points in the game fixed-width fonts are required,
such as when printing maps, etc.  To accomodate this, before printing
to the screen
.B scr_putline()
(see below) should invoke the macro
.BR F2_IS_SET(B_FIXED_FONT) . 
If this macro returns non-0 then the current buffer should be printed
in a fixed-width font; if it returns 0 then a proportional-width font
may be used if desired. 
.SS "Scripting"
All Infocom games support the
.B script
command, which is supposed to begin a transcript of the user's
adventure.  Normally this initialized the printer and all subsequent
text and commands were printed to the printer as well as to the
screen. 
.P
The TI may support this feature as well.  There's no Z-Code primitive
for turning on and off scripting, however: instead a flag is set or
reset.  In
.B scr_putline()
and
.B scr_getline()
the macro call
.B F2_IS_SET(B_SCRIPTING)
should be used; if it returns non-0 then the output line or prompt and
command should be printed to the script file as well as the display. 
.P
The interface will call
.B scr_open_sf()
with a file type of
.B SF_SCRIPT
when a script file is to be opened and
.B scr_close_sf()
when a script file is to be closed. 
.SS "Fixed Window"
Only
.I Seastalker
makes use of the fixed window feature in Standard Series games, but
it's nice to have nevertheless. 
.P
If the TI can support a fixed window in addition to the normal
scrolling text window then
.B scr_begin()
should notify the interpreter by calling
.BR F1_SETB(B_STATUS_WIN) . 
In this case the
.B scr_window()
and
.B scr_set_win()
functions described below will be used to create/manage/delete the
fixed window.
.P
If the TI cannot support a fixed window then the above macro should
not be called and
.B scr_window()
and
.B scr_set_win()
may be dummy functions.
.SH "GLOBAL VARIABLES"
These global variables are defined in the interpreter code and may be
used by the TI:
.SS gflags
Contains global flags and other general variables of interest.  The
only field which may be modified is the
.B pr_status
field, if the TI doesn't support status line printing.
.SS ti_location
This nul-terminated string is a short description of the current
location of the player, for printing on the status line.  It is
usually displayed on the left-hand side of the status line.
.SS ti_status
This nul-terminated string contains the status of the player.
Depending on the game this may be score, score and turn number, or
the current time in a 12-hour clock.  This string is usually printed
on the right-hand side of the status line.
.P
These global variables should be defined in the TI .c
file; they will be declared external where used in the interpreter
code.
.SS scr_usage
The
.B scr_usage
variable should be initialized to a string containing info about any
extra command-line options that the TI makes
available.  Here's an example from the
.I termcap
TI:
.nf

    const char *scr_usage = "[-H file] [-C file]";
.fi
.P
If there are no extra command-line options the variable should be set
to the empty string (""),
.B not
NULL.
.SS scr_long_usage
The
.B scr_long_usage
variable should be initialized to be a string containing a one-line
per option description of any extra command-line options that the TI
makes available.  Here's an example from the
.I termcap
TI:
.nf

    const char *scr_long_usage = "\e
    \et-H file\etread/store command history in file\en\e
    \et-C file\etread/store user command completions in file\en";
.fi
.P
If there are no extra command-line options the variable should be set
to NULL.
.SS scr_opt_list
The
.B scr_opt_list
variable should be initialized to be a string containing
getopt(3)-style descriptions of any extra command-line options that
the TI makes available.  Briefly, each option character is placed in
the string; if the option has an argument (no optional arguments
allowed) then it should be immediately followed by a colon (:).
Here's an example from the
.I termcap
TI:
.nf

    const char *scr_opt_list = "C:H:";
.fi
.P
If there are no extra command-line options the variable should be set
to the nul string ("").
.SH "Interpreter Macros"
The following macros are defined in
.I infocom.h
and available to the TI writer for access to certain data structures.
.P
The flags available with
.B F1_*()
macros are:
.TP
.B B_USE_TIME
If set them time is displayed on the status line, otherwise
score/moves is displayed.  This is a readonly value; the TI cannot
change it.
.TP
.B B_TANDY
The Tandy license flag; if set then Tandy licensing mode is turned on,
otherwise it's turned off.
.TP
.B B_ALT_PROMPT
The alternate prompt flag; if set then alternate prompting is enabled,
otherwise it's disabled.
.TP
.B B_STATUS_WIN
If set the TI supports fixed windows (for
.IR Seastalker ),
if not set it doesn't.
.P
The flags available with
.B F2_*()
macros are:
.TP
.B B_SCRIPTING
If set then scripting mode is currently enabled, otherwise it's
disabled.
.TP
.B B_FIXED_FONT
If set then the current buffer must be printed in a fixed-width font
if possible; otherwise it may be printed in a proportional font.
.TP
.B B_SOUND
If set the interpreter may issue
.B scr_putsound()
requests; otherwise it won't.  Note this flag is readonly and may not
be set by the TI.
.SS F?_IS_SET()
This macro returns a 0 if the specified game header flag is not set,
and a non-0 (note
.B not
necessarily 1!)
if it is not set.
.SS F?_SETB()
This macro sets the specified game header flag.
.SS F?_RESETB()
This macro resets (un-sets) the specified game header flag.
.Sh "Interpreter Functions"
The following functions are defined in the interpreter and available
to the TI writer for use (no other functions in the interpreter should
be called).
.SS xmalloc()
This function calls
.B malloc()
and exits with an error if there is no heap left.
.SS xrealloc()
This function calls
.B realloc()
and exits with an error if there is no heap left.
.SS chop_buf()
.TP
.B buf
Buffer to be chopped.
.TP
.B max
Max number of chars wanted
.P
This function locates the longest section of
.B buf
less than or equal to
.B max
characters containing at least one complete word.  It does not modify
the buffer.  It returns a pointer to the space where the line should
be broken, or to the nul character ('\e0') if
.B buf
is less than or equal to
.B max
chars long.
.P
This function is useful with fixed-width font TIs for chopping up the
buffer passed to
.B scr_putline()
into sections which fit on the screen.  See examples of its use in
.IR stream.c .
.SH "TI Functions"
The following functions need to be provided by the TI code.
.SS scr_cmdarg()
.TP
.B argc
Number of command-line args.
.TP
.B argv_p
Pointer to an array of strings; each one is a command-line argument.
.P
This function will be called before any other scr_*() function, and
before the command-line arguments are examined by the interpreter.  It
allows the TI to add (and possibly remove) arguments to the command
line.
.P
If the interface has the ability to determine command-line options
from an alternate source, such as a configuration file, an environment
variable, windowing resources, or saved game resources, then it should
add these options to
.B argv_p
so they will be noticed by the interpreter.
.P
Note that when adding options they should be inserted between
.B (*argv_p)[0]
and
.BR (*argv_p)[1] ,
shifting the existing options over, so that they will be properly
overridden by any options given on the command line.
.P
This function should return the number of elements in
.B argv_p
after modification (the new argument count).  It is expected that
after this function is called,
.B (*argv_p)[0]
remains the name of the interpreter program,
.B (*argv_p)[1]
to
.B (*argv_p)[argc-1]
are command-line arguments, and
.B (*argv_p)[argc]
is 0 (NULL).
.P
Although it is perfectly legal to remove arguments from
.B argv_p
as well, this is trickier because you must follow all UNIX getopt(1)
conventions.  It is suggested that
.B scr_getopt()
is used to process arguments on the command line, and
.B scr_cmdarg()
used only to add extra arguments if needed.
.SS scr_getopt()
.TP
.B c
The command-line option character.
.TP
.B arg
The command-line option argument (if used).
.P
This function will be called each time an option specified in
.B scr_opt_list
is found on the command line.  It is guaranteed that only options
contained in
.B scr_opt_list
will be passed, and each option which requires a command-line argument
will have one.  Error messages for poorly formed options will be
printed by the interpreter and this function will not be called.
.SS scr_setup()
.TP
.B margin
The number of spaces in the right margin.
.TP
.B indent
The number of spaces in the left margin.  Note that normally only the
output text is indented, not the command prompts.
.TP
.B lines
The number of lines per screenful.  If this value is non-0 then the
TI must make every effort to use this value as the
number of lines per screenful.  If it is 0 then the interface must
infer the screen size as best it can.
.TP
.B context
The number of lines that should be kept at the top of the screen when
scrolling; this many lines should be left at the top of each page of
output if a paged output mode is in effect.
.P
This function is called after the game has initialized and examined
its command-line arguments but before anything else is done.  It is
called for both game-playing and information modes, so it should
perform any setup necessary for general stdio printing.
.P
This function should return the number of characters that may be
printed on one line in informational mode.  If game-playing mode is to
be used then this value is ignored.
.SS scr_shutdown()
This function should perform any general end processing appropriate
for both game-playing and informational modes.
.SS scr_begin()
This function will be called just before the interpreter goes into
game-playing mode.  If the interpreter is running in information mode
this function will never be called.
.P
Any game-playing mode specific processing should be done here, such as
opening a new window, clearing the screen, setting terminal
characteristics, etc.  After this function returns the screen should
be set up and the cursor should be positioned at the lower-left hand
corner of the screen.
.P
Additionally, if the TI supports the creation of a
fixed status window (in addition to the normal status line) it should
call the macro
.B F1_SETB(B_STATUS_WIN);
here to notify the game Z-Code so that it will properly call the
.B scr_window()
and
.B scr_set_win()
functions (see below).
.SS scr_end()
This function should perform any end processing appropriate for
game-playing mode only, such as deleting windows, resetting terminal
characteristics, etc.
.SS scr_putline()
.TP
.B buffer
A nul-terminated string containing any number of characters.  The
string may be empty, but the pointer will not be NULL.  There will be
no newline character ('\en') at the end of the string.
.P
This function should print
.B buffer
to the screen, performing whatever line wrapping, paging, etc. is
necessary.  Processing should proceed as follows:
.P
For each lineful of characters in
.BR buffer :
.IP * 4
print whatever indent was specified.
.IP * 4
print a lineful along with a newline and whatever scrolling is
necessary.
.IP * 4
if the interface supports paging, and it's enabled, and it's been
(screenful - context) lines since the last prompt was printed, then
perform whatever paging is necessary.
.P
If the TI supports proportional- and fixed-width fonts
then the macro call
.B F2_IS_SET(B_FIXED_FONT)
should be used before printing
.B each
line; if the macro returns non-0 then that line must be printed in a
fixed-width font if possible (maps, etc.).  If the macro returns 0
then a proportional font may be used if desired.
.P
If the TI supports scripting then the macro call
.B F2_IS_SET(B_SCRIPTING)
should be checked; if the macro returns non-0 then
.B buffer
should be printed to the script file as well.
.SS scr_putscore()
This function is called whenever the values on the status line are to
be displayed.  If the interface supports status-line printing and it
is enabled, then this function should use the values of
.B ti_location
and
.B ti_status
to display the status line on the screen in whatever manner it
chooses (these variables always contain the proper values; they may be
used by any TI function).
.P
This function should be also be called whenever the TI
code might have disturbed the status line.
.SS scr_putsound()
.TP
.B number
The sound number to be played.
.TP
.B action
The action to be taken.
.TP
.B volume
The volume at which to play the sound (between 1 and 8).
.TP
.B argc
The number of arguments (1 to 3) passed to the function.
.P
This function will be called by the interpreter if the game being
played contains sound support.  If the interface doesn't support sound
then a simple output line may be printed by this function stating that
fact.
.P
Unfortunately different versions of Infocom games require different
methods of sound support, but generally a separate sounds file is
supplied with the datafile and the sounds must be retrieved and played
from there.
.SS scr_putmesg()
.TP
.B buffer
A nul-terminated string containing a message from the interpreter.
.TP
.B is_error
Set to 1 if the message is an error message, or 0 if it's an
informational message only.
.P
This function is called whenever the interpreter needs to print a
message for some reason.  Messages from the game will be printed via
scr_putline().  The TI should
.B not
attempt to exit the program or anything else; the interpreter will
decide what to do about the message.
.SS scr_getline()
.TP
.B prompt
A nul-terminated string which is the prompt to be printed when
requesting a command.
.TP
.B length
The maximum number of characters (including the nul character) which
can be placed in
.BR buffer .
.TP
.B buffer
A string of
.B length
characters, in which the command should be returned.
.P
This function is called when the interpreter needs command input from
the user.  The function should accept up to
.BR length -1
characters of input and place them, along with a terminating nul
character, into
.BR buffer .
All command history, editing, shell escaping, etc. etc. must be done
internal to this function.  Also if the user types more than
.BR length -1
characters the function should flush the rest of the input and notify
the user that it is doing so.
.P
If this function causes the status line to be messed up (i.e., a shell
escape causes extra lines to be printed, etc.) care should be taken to
rewrite the status line correctly.
.P
The interpreter supports an "interpreter escape mode", where
interpreter flags and options may be modified.  This mode is invoked
by the user typing the escape character (by default ``@'') as the
first character on the command line.  If this function sees that as
the first character, it should call
.B ti_escape()
with the remainder of the command line (
.B not
the escape character).  Once that function returns the prompt should
be reprinted and more input should be obtained without returning.  If
the TI wishes to provide its own escape commands it
should process them itself and not call
.B ti_escape()
for those commands.
.P
The escape char by itself will list the available options.  If the
TI wishes to supply options in addition to the
interpreter options, it should call
.B ti_escape()
first, then print its own options.
.P
Note that the TI may provide alternative ways of
manipulating these options, such as pull-down or pop-up menus on
windowing systems;
.B ti_escape()
can be called whenever control is properly passed to the TI; not from
a signal handler, etc.
.P
If the TI supports scripting, then it should check the command to see
if it was "script".  If it was and scripting is not already enabled
then it should be enabled here.  If the enabling fails then an error
should be printed and the TI should ask for another command without
returning to the interpreter.
.P
The function should return the number of characters placed into
.BR buffer ,
not counting the terminating nul character.
.SS scr_window()
.TP
.B size
Create a fixed status window
.B size
lines high.  If
.B size
is 0, then delete the current fixed status window (if any).
.P
This function is called if the game supports a fixed status window
(currently
.I Seastalker
is the only such Standard Series game), and the TI has
registered that it supports such functionality by calling
.B F1_SETB(B_STATUS_WIN)
in scr_begin() (see above).
.P
The function will be passed the number of vertical lines needed for
the fixed status window.  When the window is to be deleted it will be
passed an argument of 0.  Note it may be passed a 0 even if the window
currently doesn't exist; in this case the function should just
silently return.
.P
If the TI doesn't support a fixed status window then
just provide a dummy function which does nothing.
.SS scr_set_win()
.TP
.B win
Determines which window to start printing in.
.P
This function is called if the game supports a fixed status window
(currently
.I Seastalker
is the only such Standard Series game), and the TI has
registered that it supports such functionality by calling
.B F1_SETB(B_STATUS_WIN)
in scr_begin() (see above).
.P
After the
.B scr_window()
function has been called (see above), this function will be used to
switch back and forth between the two windows.  If called with
.B win
set to 0, then the TI should set up to start printing
in the regular text window.  If called with
.B win
set to 1, then set up to start printing in the fixed status window.
All actual printing is done with the normal
.B scr_putline()
function.
.SS scr_open_sf()
.TP
.B length
The maximum number of characters (including the nul character) which
can be placed in
.BR buffer .
.TP
.B buffer
A string of
.B length
characters, in which the filename should be returned.
.TP
.B type
The type of save file to be opened: SF_SAVE, SF_RESTORE, or SF_SCRIPT.
.P
This function is called when the interpreter needs to open a save game
file for either saving or restoring, or to open a script file.
.P
When
.B scr_open_sf()
is called,
.B buffer
will contain a default filename (initially it will be a static name,
after the first call it will contain the last filename entered).  If
.B length
is 0, then the file should just be opened and the file pointer
returned.  If
.B length
is greater than 0, the function should print an appropriate prompt,
then accept up to
.BR length -1
characters of input and place them, along with a terminating nul
character, into
.B buffer
then open that file and return the file pointer.
.P
If
.B type
is SF_SAVE or SF_SCRIPT, then the file is being opened to save the
game or write script to and so should be opened for writing (binary
mode for SF_SAVE and plain text mode for SF_SCRIPT).  The function
should test if the file already exists and if so, ask the user to
confirm overwriting it.
.P
If
.B type
is SF_RESTORE, then the file is being opened to restore a game and so
should be opened for reading in binary mode.
.P
Any command history, editing, filename completion, shell escaping,
etc. etc. must be done internal to this function.  Also if the user
types more than
.BR length -1
characters the function should flush the rest of the input and notify
the user that it is doing so.
.P
If this function causes the status line to be messed up (i.e., a shell
escape causes extra lines to be printed, etc.) care should be taken to
rewrite the status line correctly.
.P
If the TI wishes to use this function for other purposes, the
.B type
argument can be used: the interpreter will only call this function
with values >=0 for
.B type
so the TI is free to call it with special values <0 if it wishes.
.P
The function should return a FILE pointer to the open file.  If the
user cancels the operation somehow or the open fails, then the
function should return 0 (NULL).  If the return value is NULL then the
interpreter will examine
.BR errno :
if
.B errno
is 0 then the interpreter will assume the operation was canceled; if
it's non-0 then the interpreter will assume there was an error during
opening of the file and print an appropriate error message.
.P
If the function returns non-NULL, the interpreter will attempt to
read/write data from the current position in the file without
rewinding or other manipulation.  This means that you may store
TI-specific information at the beginning of saved game files and read
them back in when restoring if you like.
.SS scr_close_sf()
.TP
.B filenm
The name of the game file just saved/restored
.TP
.B fp
FILE pointer of the game just saved/restored
.TP
.B type
The type of save file to be opened: SF_SAVE, SF_RESTORE, or SF_SCRIPT.
.P
This function is called immediately after a game save file is saved or
restored or a script file is to be closed.  At the very least this
function should perform an
.B fclose(fp)
to close the file.  Additionally it may add more terminal-specific
data, create special configuration files, change file privileges, etc.
.P
If the TI wishes to use this function for other purposes, the
.B type
argument can be used: the interpreter will only call this function
with values >=0 for
.B type
so the TI is free to call it with special values <0 if it wishes.
.SH "Writing a New Interface"
If you wish to create a new TI, I suggest that you
start with stream.c as the simplest current interface.  Be sure you
examine all of this code so you understand in detail what each
function does.  You should then create your own .c file and add
functionality as you like.
.SH "MAKEFILE"
After you create your new TI you should name the .c
file something relevant, such as
.I pcdos.c
for an interface to an IBM PC-compatible in DOS, or
.I pcwindows.c
for an interface to an IBM PC-compatible running MS-Windows, or
something.  Then you should create a new TERMTYPE section in the
Makefile.  Decide what, if any, flags you need to give to the compiler
and fill in TERMFLAGS; likewise for any extra libraries you need and
TERMLIB.
.P
If you'd like your interface distributed with the pinfocom
distribution, and an interface doesn't already exist, then send it to
the pinfocom maintainer and he/she'll incorporate it in.  Note you
must place your interface code under the GNU Public License for it to
be distributed (see the copyright notices in the other files), and
provide an address for people to contact you about bugs, enhancements,
etc. in a comment in the .c file.
.P
If an interface already exists then you'll have to contact the author
and the two of you can hash it out between you :-).

.\" Local Variables:
.\" mode: nroff-mode
.\" End:
