/* $Header: d:/cvsroot/tads/html/htmlsys.h,v 1.4 1999/07/11 00:46:40 MJRoberts Exp $ */

/* 
 *   Copyright (c) 1997 by Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  htmlsys.h - HTML system API interface
Function
  These classes define a set of system-dependent functions that the
  HTML formatter needs to do its work.  These classes must be implemented
  separately for each operating and/or window system.  The purpose
  of these classes is to provide a portable interface to these system-
  dependent functions, so that the HTML formatter doesn't need to be
  modified for each new operating system that it's ported to.  These
  classes isolate the system-dependent code so that it can be easily
  found for porting purposes.

  Note that these classes are designed as abstract interfaces.  The
  reason for this design is to allow these classes to be easily used
  with an existing C++ application framework.  To use the classes from
  an application framework to implement the window object interface
  (CHtmlSysWin), for example, simply create a subclass of the
  appropriate window class from your framework, and make your subclass
  multiply inherit from CHtmlSysWin as well as from the framework
  class.  Because CHtmlSysWin is an abstract interface class, it
  should always be easy to use it in a multiple inheritance design
  without worrying about any of the problematic aspects of multiple
  inheritance (such as virtual base classes).
Notes

Modified
  09/07/97 MJRoberts  - Creation
*/

#ifndef HTMLSYS_H
#define HTMLSYS_H

#include <stdarg.h>
#include <time.h>

#ifndef HTML_OS_H
#include "html_os.h"
#endif
#ifndef TADSHTML_H
#include "tadshtml.h"
#endif


/* ------------------------------------------------------------------------ */
/*
 *   Font description.  This is used to specify the characteristics of a
 *   desired font to a window, so that the window can create an
 *   appropriate system font appropriate for the description.
 *   
 *   The oshtml_charset_id_t type is a system-defined type that specifies
 *   the character set to be used for the font.  On systems such as
 *   Windows where fonts are mapped to particular code pages, we use this
 *   to select a font that contains the code page mapping that we need for
 *   each text string.
 *   
 *   Note that this is a portable concrete class - there is no need to
 *   subclass this class per platform.  This class is defined to provide a
 *   portable means of describing a font, so that an appropriate system
 *   font object can be created based on a description generated by
 *   portable code.  
 */
const int HTMLFONTDESC_FACE_MAX = 128;
class CHtmlFontDesc
{
public:
    CHtmlFontDesc()
    {
        face[0] = '\0';
        face_set_explicitly = FALSE;
        pointsize = 0;
        default_color = TRUE;
        default_bgcolor = TRUE;
        color = 0;
        bgcolor = 0;
        weight = 400;
        italic = FALSE;
        underline = FALSE;
        strikeout = FALSE;
        fixed_pitch = FALSE;
        serif = TRUE;
        htmlsize = 0;
        superscript = subscript = FALSE;
        pe_big = pe_small = FALSE;
        pe_em = pe_strong = pe_dfn = pe_code = pe_samp = pe_kbd
            = pe_var = pe_cite = pe_address = FALSE;
        default_charset = TRUE;
    }

    void copy_from(const CHtmlFontDesc *src);

    /*
     *   Size of the font in points.  Note that we will ignore the
     *   pointsize if the htmlsize member is set to a non-zero value.  
     */
    int pointsize;

    /*
     *   Weight, on a scale of 0 to 1000.  The weights available for a
     *   particular font vary, but by convention the following weights
     *   apply to most faces: 200 is light; 400 is normal or book weight;
     *   700 is bold; 900 is black (ultrabold).  
     */
    int weight;

    /* italic, underline, strike-out */
    int italic : 1;
    int underline : 1;
    int strikeout : 1;

    /*
     *   Color.  If default_color is TRUE, we'll ignore this and use an
     *   appropriate default text color instead.
     *   
     *   Note that font descriptors only contain RGB values.  Special color
     *   ID values (the HTML_COLOR_xxx parameterized color IDs) are not used
     *   in font descriptors.  
     */
    int default_color : 1;
    HTML_color_t color;

    /*
     *   Background color.  If default_bgcolor is TRUE, we'll ignore this and
     *   draw text transparently over the existing background. 
     *   
     *   Note that font descriptors only contain RGB values.  Special color
     *   ID values (the HTML_COLOR_xxx parameterized color IDs) are not used
     *   in font descriptors.  
     */
    int default_bgcolor : 1;
    HTML_color_t bgcolor;

    /*
     *   Face name.  If the face name is provided, we'll use the face to
     *   choose a font and ignore the characteristics.  If the face name
     *   is left empty, we'll use the characteristics to choose a font.
     *   Multiple face names can be specified by separating the names with
     *   commas; the first face that is available on the machine at
     *   run-time is used.
     *   
     *   Note that the following parameterized font names can be used.
     *   These names must be mapped to an appropriate actual font on each
     *   system.  The purpose of these names is to allow games more
     *   control over choosing a specific style of font while retaining
     *   portability and also giving the player more control of the
     *   presentation.  When possible, the player should be allowed to
     *   select the actual system font to be used for each of these fonts
     *   through a preferences mechanism.  Note that these parameterized
     *   font names are mapped to actual system fonts by
     *   CHtmlSysWin::get_font().
     *   
     *   TADS-serif - a serifed font, usually proportional
     *.  TADS-sans - a san serif font, usually proportional
     *.  TADS-script - a script or cursive font, usually proportional
     *.  TADS-typewriter - a typewriter-style font, usually fixed-width
     *.  TADS-input - the font to use for player commands
     */
    textchar_t face[HTMLFONTDESC_FACE_MAX];

    /* 
     *   The parameterized font names.  Note that, as with tag and attribute
     *   names, these names are to be parsed WITHOUT case sensitivity.  
     */
#define HTMLFONT_TADS_SERIF        "TADS-serif"
#define HTMLFONT_TADS_SANS         "TADS-sans"
#define HTMLFONT_TADS_SCRIPT       "TADS-script"
#define HTMLFONT_TADS_TYPEWRITER   "TADS-typewriter"
#define HTMLFONT_TADS_INPUT        "TADS-input"

    /*
     *   Flag: the face was set explicitly in the descriptor.  This is set
     *   in cases where the face name is being explicitly changed, rather
     *   than inherited from an enclosing block of text.  When this is set
     *   to true, if the face is a parameterized font name (such as
     *   "TADS-input"), and the parameterized font carries other attributes
     *   such as color and italics settings, we will apply the other
     *   attributes.  When this is set to false, we'll use the parameterized
     *   face name, but we'll otherwise ignore the extra baggage carried
     *   with the face, if any.
     *   
     *   For example, suppose that we're formatting along in a string of
     *   text, and we want to change the text color to red for a bit of
     *   text, but we want to continue using the other attributes of the
     *   text up to this point; in such a case, we'd copy the font
     *   descriptor currently in use, so we'd fill in the current face name,
     *   but we'd leave this flag set to false to indicate that we don't
     *   want any of the baggage associated with the font if it's a
     *   parameterized font.  
     */
    int face_set_explicitly : 1;

    /*
     *   Characteristics.  In the absence of an explicit face name, we
     *   can use these characteristics to choose a font suitable for a
     *   particular HTML usage.  If the face name is provided, these
     *   characteristics are ignored.  
     */

    /* true -> fixed pitch, false -> variable pitch */
    int fixed_pitch : 1;

    /* true -> serif face, false -> sans serif face */
    int serif : 1;

    /*
     *   HTML size, on a scale of 1 to 7.  If this value is zero, the
     *   pointsize member will be used instead.  
     */
    int htmlsize;

    /* superscript/subscript (mutually exclusive) */
    int superscript : 1;
    int subscript : 1;

    /* HTML BIG/SMALL settings (mutually exclusive) */
    int pe_big : 1;
    int pe_small : 1;

    /* HTML phrase/block elements that select a font */
    int pe_em : 1;
    int pe_strong : 1;
    int pe_dfn : 1;
    int pe_code : 1;
    int pe_samp : 1;
    int pe_kbd : 1;
    int pe_var : 1;
    int pe_cite : 1;
    int pe_address : 1;

    /* 
     *   Character set identifier; this is system-dependent, and is opaque
     *   to the generic code.  Character set identifiers can be obtained
     *   from the system (for example, via the CHtmlSysWinGroup method
     *   get_default_win_charset()), and can then be passed back to the
     *   system when, for example, creating a font.
     *   
     *   When a font descriptor is used to create a font object,
     *   'default_charset' can be set to TRUE to use the default system
     *   character set.  In this case, 'charset' is ignored.
     *   
     *   Once a font object is created, the system must set 'charset' to a
     *   valid character set if 'default_charset' is specified in the
     *   creation descriptor.  This allows a caller to obtain the actual
     *   character set for a font object, even if the font was created with
     *   the default flag (and thus no valid 'charset' data).  
     */
    oshtml_charset_id_t charset;
    int default_charset;
};


/* ------------------------------------------------------------------------ */
/*
 *   System font object.  This is an abstract interface; each system must
 *   have its own implementation of the interface.
 *   
 *   Note that instances of (concrete subclasses of) CHtmlSysFont are created
 *   by the system window code.  Refer to CHtmlSysWin::get_font().
 *   
 *   A note on memory management: The portable code never deletes font
 *   objects.  Instead, the system code is expected to keep a list of all of
 *   the font objects ever created.  Each time a caller asks to create a new
 *   font via CHtmlSysWin::get_font(), the system code first scans the list
 *   to see if the same font (with the identical descriptor) has been
 *   allocated already; if so, it simply returns the existing CHtmlSysFont
 *   instance.  If not, it creates a new CHtmlSysFont instance, adds it to
 *   the list, and returns the new instance.
 *   
 *   So, even though the portable code never deletes font objects, it won't
 *   leak memory as long as the system code uses this cache list approach.
 *   Font memory will never shrink, but it will only grow up to a fixed upper
 *   limit - specifically, the maximum font memory will be the amount needed
 *   to hold one instance of each distinct font used in the game.
 *   
 *   The reasoning behind this approach is that (a) a given game has a
 *   relatively small number of distinct fonts it uses, (b) each font tends
 *   to be used over and over many times, and (c) on many systems it's a
 *   relatively heavy operation to create a system font resource.  So it's
 *   more efficient to use this cache approach than to create and destroy
 *   font objects on each use.  Fonts objects would also be fairly complex to
 *   account for, so this scheme avoids the need for reference counting or
 *   other means to avoid font memory leaks.  
 */
class CHtmlSysFont
{
public:
    virtual ~CHtmlSysFont() { }

    /* get the metrics for the font */
    virtual void get_font_metrics(class CHtmlFontMetrics *) = 0;

    /* 
     *   Is this a fixed-pitch font?  This indicates whether or not the
     *   actual system font that this object represents is monospaced.  For
     *   efficiency, implementations should (if possible) note the font's
     *   type upon instantiation of this object, and simply return the
     *   remembered information here.  
     */
    virtual int is_fixed_pitch() = 0;

    /*
     *   Get the "em" size of the font, expressed in pixels.  On many
     *   systems, a font's em size is a design property of the font, stored
     *   with the font's OS-level resource (or equivalent) information.
     *   
     *   For systems where the em size isn't stored with the font,
     *   implementations should simply return the nominal point size of the
     *   font translated to a pixel size.  In traditional typography, a
     *   font's em size is the same as the font's nominal height.
     *   
     *   For efficiency, implementations should (if possible) note the em
     *   size upon instantiation of this object, and simply return the stored
     *   size information here.  
     */
    virtual int get_em_size() = 0;

    /* 
     *   Get the description of the font (or a pointer to it).  This is the
     *   concrete description, which isn't necessarily identical to the
     *   descriptor that was used to create the font, since the font creation
     *   process might have resolved parameterized font names (TADS-Input,
     *   TADS-Sans, etc) and filled in concrete values for defaults.  
     */
    void get_font_desc(class CHtmlFontDesc *dst) const
        { dst->copy_from(&desc_); }
    const CHtmlFontDesc *get_font_desc_ref() const { return &desc_; }

    /*
     *   Get the color, and whether this color should be used (if not, an
     *   appropriate default color should be used instead).
     */
    HTML_color_t get_font_color() const { return desc_.color; }
    int use_font_color() const { return !desc_.default_color; }

    /* get the character set of the font */
    oshtml_charset_id_t get_charset() const { return desc_.charset; }

    /* 
     *   Get the background color, and whether this color should be used.  If
     *   the background color is not to be used, the text should be displayed
     *   over the existing background.  If the background color is to be
     *   used, then the background color should fill in the bounding
     *   rectangle of the text. 
     */
    HTML_color_t get_font_bgcolor() const { return desc_.bgcolor; }
    int use_font_bgcolor() const { return !desc_.default_bgcolor; }

protected:
    /* 
     *   the font descriptor - the system must set this to the font
     *   descriptor when the font is created 
     */
    CHtmlFontDesc desc_;
};



/*
 *   Font metrics (portable class; this is used to parameterize certain
 *   functions that measure text displays).  
 *   
 *   The 'ascent height' is the distance between the TOP of the box
 *   containing any character in the font and the BASELINE.  The baseline is
 *   defined by the font's designer, but in general it's the alignment point
 *   for the bottoms of the capital letters and of the minuscules that don't
 *   have tails (tails: g, j, p, q, y - the bits that drop below the
 *   baseline).  The reason the ascent height is important is that it tells
 *   us where the baseline is relative to the overall rectangle containing a
 *   character in the font, and the baseline is important because it's the
 *   reference point for aligning adjacent text from different fonts.
 *   
 *   The 'descent height' is the height below the baseline.
 *   
 *   The total height is the sum of the ascender and descender heights.  
 */
class CHtmlFontMetrics
{
public:
    int ascender_height;                /* height in pixels above baseline  */
    int descender_height;                /* height in pixels below baseline */
    int total_height;                             /* total height in pixels */
};


/* ------------------------------------------------------------------------ */
/*
 *   Enumeration status code return values for status functions.  These are
 *   not currently used in the portable interface, but are included here for
 *   possible future use, and can be used within the platform-specific
 *   implementation if convenient.
 *   
 *   These are obsolescent - the corresponding OS_EVT_xxx status codes
 *   should be used instead.  
 */
enum htmlsys_input_stat_t
{
    /* input successfully read */
    HTMLSYS_INPUT_STAT_OK,

    /* end of file, application terminated, or other error reading input */
    HTMLSYS_INPUT_STAT_EOF,

    /* operation timed out - input interrupted before user pressed "Enter" */
    HTMLSYS_INPUT_STAT_TIMEOUT
};


/* ------------------------------------------------------------------------ */
/*
 *   Banner position settings.  Banners can be horizontal, in which case they
 *   are above or below the main window and run across the whole width of the
 *   main window; or vertical, in which case they are left or right of the
 *   main window and run down its entire height.
 *   
 *   Multiple banners can be created with the same alignment, in which case
 *   each new banner is aligned just inside of the previous banner.  ("Inside
 *   of" in the sense of relative to the boundaries of the outer frame
 *   window.)
 *   
 *   For example, the first top-aligned banner goes at the top of the main
 *   window, with the second top-aligned banner aligned immediately below it,
 *   and the input window immediately below that.  Similarly, the first
 *   right-aligned banner is aligned at the right edge of the main window,
 *   the second right-aligned banner is just to the left of the first, and
 *   the main window is just to the left of those.
 *   
 *   Every window is always rectangular.  To determine the positioning of the
 *   banners, start off with the input window equal in size to the overall
 *   main window.  For each banner, divide the input window's area according
 *   to the type of banner and its size.  For example, for a bottom-aligned
 *   banner, divide the main window into two horizontal bands -- one for the
 *   banner and one for the remaining input window -- each the full width of
 *   the input window; the banner is given an amount of vertical space
 *   determined by its height, and the remainder is left for the new input
 *   window area.  Shrink the input window to the new size left over after
 *   removing the new banner's area from the original input window area.  Now
 *   apply the same process to each subsequent banner window.
 *   
 *   Note that these codes are mapped directly to the corresponding
 *   OS_BANNER_ALIGN_xxx codes, to simplify using our banner window system as
 *   an implementation of the tads 2 os_banner interface.  
 */
enum HTML_BannerWin_Pos_t
{
    /*
     *   Default alignment - the banner is positioned at the top of the main
     *   window, and runs across the whole width of the main window 
     */
    HTML_BANNERWIN_POS_TOP = OS_BANNER_ALIGN_TOP,

    /* banner is at the bottom of the main input window */
    HTML_BANNERWIN_POS_BOTTOM = OS_BANNER_ALIGN_BOTTOM,

    /*
     *   The banner divides the main window vertically, and is aligned at the
     *   left edge of the main window.  The banner runs down the entire
     *   height of the main window.  
     */
    HTML_BANNERWIN_POS_LEFT = OS_BANNER_ALIGN_LEFT,

    /* vertical banner aligned at the right edge of the main window */
    HTML_BANNERWIN_POS_RIGHT = OS_BANNER_ALIGN_RIGHT
};

/*
 *   Banner window size units.  These are used to specify the interpretation
 *   of the size value when a size is specified.  
 */
enum HTML_BannerWin_Units_t
{
    /*
     *   Percentage size.  The size is given as a percentage of the REMAINING
     *   space available in the parent window.  
     */
    HTML_BANNERWIN_UNITS_PCT,

    /*
     *   "Character" size.  The size is given in rows or columns of
     *   characters.  For a window with proportionally-spaced text, or
     *   varying text sizes, this is in terms of the size of a '0' character
     *   in the window's default font. 
     */
    HTML_BANNERWIN_UNITS_CHARS,

    /*
     *   Pixel size.  The size is given in pixels.
     */
    HTML_BANNERWIN_UNITS_PIX
};


/*
 *   Banner window type codes.  When a banner window is created, one of these
 *   type codes will be passed to the system code to let it know what's going
 *   on in the window.
 */
enum HTML_BannerWin_Type_t
{
    /* 
     *   <BANNER> tag window.  This type of window is created for an in-line
     *   <BANNER> tag in the main window's HTML.  This type of window is
     *   based on contents from the same output stream as the main game
     *   window.  
     */
    HTML_BANNERWIN_BANNERTAG,

    /* 
     *   Text stream window, created programmatically (such as through the
     *   os_banner API).  This type of window has an independent output
     *   stream from the main game window, but otherwise behaves pretty much
     *   the same as a <BANNER> tag window.  
     */
    HTML_BANNERWIN_TEXT,

    /*
     *   Text grid window.  This type of window doesn't have an HTML parser,
     *   although it does use the same list of display items (CHtmlDisp
     *   objects) as any other window, so the OS window implementation
     *   doesn't usually have to do anything extra to support this.  
     */
    HTML_BANNERWIN_TEXTGRID
};

/* ------------------------------------------------------------------------ */
/*
 *   System application frame interface.  This interface must be implemented
 *   by some object in the system-dependent layer to provide the interface
 *   that the HTML version of the TADS display OS interface layer uses to
 *   operate the HTML display.
 *   
 *   This interface can be implemented by the main system window object
 *   (i.e., the same C++ object that implements the CHtmlSysWin interface),
 *   if desired; alternatively, it can be implemented by another object, such
 *   as the main application object (if there is one in the system-dependent
 *   implementation).
 *   
 *   Note: the system-dependent code is responsible for creating a singleton
 *   CHtmlSysFrame object.  After creating the object, the system code must
 *   call CHtmlSysFrame::set_frame_obj() to tell the oshtml layer about the
 *   object.  Similarly, the system code must call
 *   CHtmlSysFrame::set_frame_obj(0) when the frame object is about to be
 *   deleted, to ensure that the portable code doesn't attempt to make any
 *   calls to it after it's gone.  
 */
class CHtmlSysFrame
{
public:
    virtual ~CHtmlSysFrame() {}

    /*
     *   Set the system frame object.
     *   
     *   During initialization, the system-specific code must call this
     *   routine to initialize the frame object as soon as the frame object
     *   has been created.  (The system-specific code is responsible for
     *   creating the frame object in the first place, too.)
     *   
     *   During termination, the system code should call this to set the
     *   frame object to a null pointer so we know it's forgotten.  
     */
    static void set_frame_obj(CHtmlSysFrame *frame) { app_frame_ = frame; }

    /* get the app frame object */
    static CHtmlSysFrame *get_frame_obj() { return app_frame_; }

    /*
     *   Kill the process.  This is a static function that must be
     *   implemented by the port-specific code.
     *   
     *   This is called when the input layer (in oshtml.cpp) finds that the
     *   program is looping on input after EOF.  This routine should force
     *   the program to terminate, by explicitly killing the process via the
     *   OS API if possible.
     */
    static void kill_process();

    /* 
     *   Flush the text output buffer.
     *   
     *   If 'fmt' is true, format the new text and add it to the display;
     *   otherwise, simply parse it.
     *   
     *   If 'immediate_redraw' is true, the contents of the window should be
     *   updated on the display immediately if possible.  This is used to
     *   ensure that the display is updated before a possibly lengthy
     *   computation; on event-driven operating systems, we might not be
     *   able to process any redraw events during the computation, so we
     *   want to ensure that any necessary drawing is done before the
     *   computation begins to avoid the appearance that the program is
     *   frozen during the delay.  
     */
    virtual void flush_txtbuf(int fmt, int immediate_redraw) = 0;

    /* get the parser associated with the main window */
    virtual class CHtmlParser *get_parser() = 0;

    /* 
     *   Start a new page.  This clears out the main text window, including
     *   any banner windows created with <BANNER> tags in the main text
     *   window.
     *   
     *   Note that this should NOT remove banner windows created through the
     *   os_banner API - it should ONLY remove <BANNER> tag windows.  Most
     *   implementations will simply call remove_all_banners() on the main
     *   text window's CHtmlFormatter object; this will automatically remove
     *   the <BANNER> tag windows and preserve any os_banner API windows.  
     */
    virtual void start_new_page() = 0;

    /* 
     *   Set non-stop mode.  The system window must handle MORE prompting, so
     *   it will need to keep track of this non-stop mode setting.  
     */
    virtual void set_nonstop_mode(int flag) = 0;

    /* 
     *   Display the string of the given byte length.  Note that because an
     *   explicit length is given, the string might not be null-terminated.  
     */
    virtual void display_output(const textchar_t *buf, size_t len) = 0;

    /* 
     *   Check for a break key sequence.  Returns true if the
     *   (system-defined) break key has been hit, false if not.  
     */
    virtual int check_break_key() = 0;

    /*
     *   Read a keyboard command.  Returns false if the application is
     *   quitting, true otherwise.
     */
    virtual int get_input(textchar_t *buf, size_t bufsiz) = 0;

    /*
     *   Read a keyboard command, with an optional timeout.  This is
     *   essentially an implementation of os_gets_timeout() and behaves in
     *   essentially the same way; refer to osifc.h for full details on that
     *   routine.
     *   
     *   Returns an OS_EVT_xxx code as follows:
     *   
     *   OS_EVT_LINE - we successfully read a line of input, terminated by
     *   the user pressing Return (or the local equivalent).  If the user
     *   entered data in some other fashion that would normally work in
     *   get_input(), such as clicking on a hyperlink or selecting a command
     *   from a menu, this routine still returns OS_EVT_LINE; we are not
     *   interested in any arbitrary events, but only complete input lines.
     *   The buffer should be filled in with the line of text the user
     *   entered.
     *   
     *   OS_EVT_EOF - an error occurred, or the application is being
     *   terminated.  
     *   
     *   OS_EVT_TIMEOUT - the timeout expired without the user having
     *   completed input.  The buffer should be filled in with the text the
     *   user entered before the timeout expired.  Everything should be left
     *   as-is in the user interface, in case we resume editing the same
     *   input with no intervening operations.  This routine must statically
     *   store its complete editing state, including the text being edited,
     *   caret position, selected text range, insert/overwrite mode, and
     *   anything else that is significant, so that the routine can restore
     *   the same editing state seamlessly if the routine is invoked
     *   subsequently without an intervening call to get_input_cancel().
     *   
     *   Note that the timeout is optional.  If use_timeout is FALSE, the
     *   routine should behave roughly the same as the regular get_input(),
     *   except that this routine should still resume the previously
     *   interrupted editing session if there is one.  
     */
    virtual int get_input_timeout(textchar_t *buf, size_t buflen,
                                  unsigned long timeout, int use_timeout) = 0;

    /*
     *   Cancel input that was interrupted by a timeout.  This routine
     *   terminates the input operation that was interrupted, making any
     *   associated display changes (such as moving the caret to a new line
     *   and removing the selected text range) that would normally be made
     *   when editing is finished on a line of text.
     *   
     *   If 'reset' is true, it means that we are to forget all of our
     *   static editing state from the interrupted editing, so that the next
     *   call to get_input_timeout() starts with an empty line of text.  If
     *   'reset' is false, then the next call to get_input_timeout() will
     *   resume the previous editing session.
     *   
     *   (The distinction between "canceling" and "resetting" is subtle.
     *   Canceling simply finishes the previous editing in terms of the user
     *   interface state, so that new display operations can be performed;
     *   the effect is the same as though the user had pressed Return to
     *   terminate the input that timed out, except that we don't forget the
     *   input.  After canceling, when we call get_input_timeout(), we
     *   re-display the saved input from the interrupted session, and resume
     *   the session where we left off, but with a freshly-displayed copy of
     *   the command line on the screen.  Resetting does everything that
     *   canceling does, but in addition forgets all of the state from the
     *   interrupted editing session, so that the next get_input_timeout()
     *   simply starts from scratch with an empty input buffer.)
     *   
     *   This routine MUST be called before any display operation (input or
     *   output) can be performed after get_input_timeout() returns
     *   OS_EVT_TIMEOUT, *except* that os_input_timeout() can be called
     *   again to resume the interrupted session.  
     */
    virtual void get_input_cancel(int reset) = 0;
    
    /*
     *   Read an input event.  This routine follows the semantics of the
     *   TADS os_get_event() interface, and returns one of the OS_EVT_xxx
     *   event types.  
     */
    virtual int get_input_event(unsigned long timeout_in_milliseconds,
                                int use_timeout, os_event_info_t *info) = 0;

    /* 
     *   Wait for a keystroke from the main window.  Returns the character
     *   corresponding to the keystroke, if it's a "normal" (i.e., ascii)
     *   character, zero if it's a system-specific keystroke, such as a
     *   function key.  If 'pause_only' is true, it means that the caller
     *   doesn't actually care about the keystroke returned, but is asking
     *   for a keystroke purely for the purpose of pausing until the user
     *   acknowledges a message by hitting a key; the window may want to
     *   display an appropriate status line message or provide a similar
     *   indication that we're waiting for a player keystroke.  
     */
    virtual textchar_t wait_for_keystroke(int pause_only) = 0;

    /* 
     *   Pause before exiting.  Wait for a keystroke.  This is similar to
     *   wait_for_keystroke(); it's a separate routine in case the window
     *   wants to display something to notify the user that the program
     *   will exit on the next keystroke, such as a status line message to
     *   this effect.  
     */
    virtual void pause_for_exit() = 0;

    /*
     *   Display the MORE prompt, wait for the user to respond, and remove
     *   the MORE prompt.
     */
    virtual void pause_for_more() = 0;

    /*
     *   Display a message to the debug log window.  The debug window is
     *   optional, so these functions can have empty implementations.  The
     *   debug window is used to display internal HTML TADS status
     *   messages when the system is compiled with the macro
     *   TADSHTML_DEBUG; in addition, it's used to display HTML error
     *   messages that game authors may find helpful in debugging their
     *   games. 
     */
    virtual void dbg_print(const char *msg) = 0;

    /*
     *   Create a banner window.  Return null if this is not possible.
     *   
     *   'parent' is the parent window.  If this is null, then the new
     *   banner is a child of the main game window (the text window
     *   containing the main game transcript).  Otherwise, 'parent' is an
     *   existing banner subwindow; the new window is carved out of the
     *   space of the parent.
     *   
     *   'where' specifies where the banner goes in the list of children of
     *   the given parent, which determines how the space is divided among
     *   the windows.  This is OS_BANNER_FIRST to make the new banner window
     *   the first child of its parent, OS_BANNER_LAST to make it the last
     *   child, OS_BANNER_BEFORE to put it immediately before the banner
     *   window specified by 'other', or OS_BANNER_AFTER to put it
     *   immediately after 'other'.  When BEFORE or AFTER are specified,
     *   'other' must be an existing child of the given parent; if 'other'
     *   isn't a child of the parent, then the routine should behave as
     *   though OS_BANNER_LAST were specified.
     *   
     *   'pos' gives the alignment type of the banner.
     *   
     *   'style' is a combination of OS_BANNER_STYLE_xxx flags, as defined in
     *   osifc.h (from the tads 2 sources).  
     */
    virtual class CHtmlSysWin
        *create_banner_window(class CHtmlSysWin *parent,
                              HTML_BannerWin_Type_t window_type,
                              class CHtmlFormatter *formatter,
                              int where, class CHtmlSysWin *other,
                              HTML_BannerWin_Pos_t pos,
                              unsigned long style) = 0;

    /*
     *   "Orphan" a banner window.  This is simply an implementation of
     *   os_banner_orphan() from the tads 2 osifc definition.
     *   
     *   A valid implementation is simply to call os_banner_delete(),
     *   passing the formatter object as the banner handle.
     *   
     *   If the OS implementation wishes to keep orphaned banner windows on
     *   the screen for a while after they're orphaned, it can take
     *   ownership of the banner here, and delete the banner at leisure.  
     */
    virtual void orphan_banner_window(
        class CHtmlFormatterBannerExt *banner) = 0;

    /*
     *   Create a window for an "about box."  Return null if this is not
     *   possible.  If the system has a customary way of displaying an "about
     *   box" (a dialog that displays information on the application, such as
     *   its name, version, and copyright information), the window returned
     *   should be used when the about box is needed.  This window will be a
     *   standard HTML display window, which the game will fill in with HTML
     *   as it desires.
     *   
     *   Only one about box is needed for the entire application; if an about
     *   box subwindow already exists, this routine should simply return the
     *   existing window.  Note that this about box is for information about
     *   the *game*, so any menu item that accesses it should say something
     *   like "About This Game" rather than "About HTML TADS."  
     */
    virtual CHtmlSysWin
        *create_aboutbox_window(class CHtmlFormatter *formatter) = 0;

    /*
     *   Remove a banner subwindow.  This call can be ignored if the window
     *   doesn't support banners.  The CHtmlSysWin object must be deleted by
     *   this call, so the caller cannot refer to the window after this call
     *   returns.  
     */
    virtual void remove_banner_window(CHtmlSysWin *win) = 0;

    /*
     *   Look up an "embedded resource."  This is a resource (an image file,
     *   sound file, etc) that's bundled with the interpreter application
     *   itself - not part of a game, but part of the interpreter system.
     *   
     *   The resource finder calls this routine to resolve any resource name
     *   that can't be found in the game file (the .GAM or .T3 file).  In
     *   most cases, the system will want to adopt a local naming convention;
     *   on Windows, for example, these names are all prefixed with "exe:",
     *   but each system can use its own convention, as the only uses of
     *   these resources are in the system-specific code to begin with.
     *   
     *   If the resource is found, copy the name of the OS file containing
     *   the resource into the caller's buffer, set *seek_pos and *siz to the
     *   seek offset within the file and the size (in bytes) of the
     *   resource's data within the file, respectively, and return true.  If
     *   there is no such embedded resource, return false.
     *   
     *   This function is purely for the use of the system-specific
     *   interpreter implementation.  If the interpreter doesn't have any use
     *   for embedded resources, this need not be implemented.  This function
     *   was originally created to allow HTML TADS on Windows to use an HTML
     *   window for its "About" box - this routine is used to allow the HTML
     *   in the About box to refer to a JPEG image file that's embedded in
     *   the interpreter .EXE file.  This function is called from the
     *   portable code, but only in response to HTML text that's generated by
     *   the platform-specific code - so if your platform doesn't generate
     *   any such HTML source in its own internal dialogs or whatever, this
     *   will never be called.  
     */
    virtual int get_exe_resource(const textchar_t *resname, size_t resnamelen,
                                 textchar_t *fname_buf, size_t fname_buf_len,
                                 unsigned long *seek_pos,
                                 unsigned long *siz) = 0;

private:
    /*
     *   The system frame is a singleton object, defined and managed by the
     *   OS code, that implements the CHtmlSysFrame interface.  The OS code
     *   must create this object at startup and delete it at termination.  
     */
    static CHtmlSysFrame *app_frame_;
};


/* ------------------------------------------------------------------------ */
/*
 *   Window Group interface.  This interface should be associated with the
 *   object that contains the main HTML window and any banner subwindows.
 *   
 *   Depending on how the system code is designed, this interface could be
 *   associated with the same object as the CHtmlSysWin interface for the
 *   main HTML window, or could be associated with the entire application.
 *   This interface is provided separately from either of those two
 *   interfaces to allow for the possibility of an intermediate container.
 *   (This is how the Windows implementation is designed - the
 *   intermediate container object is a container window that provides a
 *   single system window frame for the main HTML window and its
 *   associated banner subwindows.)  
 */
class CHtmlSysWinGroup
{
public:
    virtual ~CHtmlSysWinGroup() {}

    /*
     *   Get the current default character set ID for the frame.  This
     *   will normally reflect the system-wide localization settings,
     *   although on some systems, the application may wish to provide a
     *   user preference to change the default character set, on an
     *   application-wide basis or possibly even per window, depending on
     *   system localization conventions.  
     */
    virtual oshtml_charset_id_t get_default_win_charset() const = 0;

    /*
     *   Translate an HTML 4 character code point value to a local
     *   character code point.  This is used to map '&' entities, both
     *   named and numeric, to local character set code points.  The HTML
     *   4 character codes are the same as Unicode values.  HTML TADS
     *   attempts to support at least the ISO Latin-1 character set plus
     *   the HTML 4 named symbol entity character set.
     *   
     *   If 'charset' is not null, this routine should fill in *charset
     *   with the OS-specific character set ID to use for the character.
     *   If 'charset' is null, the caller cannot change the character set,
     *   so we must attempt to map the character to the current default
     *   character set.  If the character cannot be mapped (either because
     *   no mapping is available at all, or because charset is null and
     *   the character can't be mapped to the current default character
     *   set), this routine should produce a suitable invalid character
     *   value.
     *   
     *   If 'charset' is not null, 'changed_charset' should be non-null as
     *   well.  On return, we'll set *changed_charset to true if we
     *   returned a non-default character set in *charset, false otherwise.
     *   
     *   The return value is the length in bytes of the result.  This
     *   routine is allowed to return more than a single byte to allow for
     *   multi-character approximations; for example, a copyright symbol
     *   could be approximated as "(c)" when the local character set
     *   doesn't have a suitable character value.  
     */
    virtual size_t xlat_html4_entity(textchar_t *result, size_t result_size,
                                     unsigned int charval,
                                     oshtml_charset_id_t *charset,
                                     int *changed_charset) = 0;
};

/* ------------------------------------------------------------------------ */
/*
 *   System window object.  This is an abstract interface; each system must
 *   have its own implementation of the interface.
 *   
 *   Note that, for simplicity, the same system window interface is used for
 *   regular HTML windows and HTML banner windows.
 *   
 *   Instances of (concrete subclasses of) CHtmlSysWin are created by system
 *   code.  The startup code (which is always system-specific) creates the
 *   main game window, and CHtmlSysFrame::create_banner_window() creates
 *   banner windows.  
 */


/*
 *   for inval_doc_coords, these values can be used for the .right and
 *   .bottom settings to indicate that the window should be invalidated
 *   all the way to the right or all the way to the bottom, respectively 
 */
const long HTMLSYSWIN_MAX_RIGHT = -1;
const long HTMLSYSWIN_MAX_BOTTOM = -1;

/*
 *   Bullet styles 
 */
enum HTML_SysWin_Bullet_t
{
    HTML_SYSWIN_BULLET_SQUARE,                          /* filled-in square */
    HTML_SYSWIN_BULLET_CIRCLE,                            /* circle outline */
    HTML_SYSWIN_BULLET_DISC,                            /* filled-in circle */
    HTML_SYSWIN_BULLET_PLAIN                    /* plain - no bullet at all */
};

class CHtmlSysWin
{
public:
    CHtmlSysWin(class CHtmlFormatter *formatter)
        { formatter_ = formatter; }
    virtual ~CHtmlSysWin() { }

    /* get the formatter associated with the window */
    class CHtmlFormatter *get_formatter() const { return formatter_; }

    /* 
     *   forget my formatter; since the container application owns the
     *   formatter, it will have to delete it at some point, and can use this
     *   routine to notify the window that the formatter is about to be
     *   deleted 
     */
    void forget_formatter() { formatter_ = 0; }

    /* get the window group object for the group I'm associated with */
    virtual class CHtmlSysWinGroup *get_win_group() = 0;

    /* 
     *   Receive notification that the contents of the window are being
     *   cleared.  The portable code calls this to let the system layer know
     *   that it's clearing the window.
     *   
     *   The system code isn't required to do anything here; this is purely a
     *   notification to let the system code take care of any necessary
     *   internal bookkeeping.  For example, a window might want to clear its
     *   internal MORE mode counter here, if it uses one, to reflect the fact
     *   that we're starting over with fresh content.  
     */
    virtual void notify_clear_contents() = 0;

    /*
     *   Close the window.  Returns true if the window successfully
     *   closed, false if not; the window may not be closed, for example,
     *   if the user cancelled a "save changes" dialog.  If 'force' is true,
     *   the window should be closed without asking the user any questions
     *   (about saving work, for example).  This should always return true
     *   if 'force' is true.  
     */
    virtual int close_window(int force) = 0;

    /* get the current width of the HTML display area of the window */
    virtual long get_disp_width() = 0;

    /* get the current height of the HTML display area of the window */
    virtual long get_disp_height() = 0;

    /* get the number of pixels per inch */
    virtual long get_pix_per_inch() = 0;

    /*
     *   Get the bounds of a string rendered in the given font.  Returns a
     *   point, with x set to the width of the *string* as rendered, and y
     *   set to the height of the *font*.  The returned point's y value
     *   should give the total height of the text, including ascenders and
     *   descenders.  If 'ascent' is not null, it should be filled in with
     *   the portion of the height above the text baseline (i.e., the
     *   ascender size for the font).
     *   
     *   The width returned should indicate the *positioning* width of the
     *   text, which might not necessarily be the same as the bounding box of
     *   the glyphs contained in the text.  The difference is that the
     *   positioning width tells us where the next piece of text will go if
     *   we're drawing a string piecewise.  For example, suppose we have a
     *   string "ABCDEF", and we call draw_text() to render it at position
     *   (0,0).  Now suppose we have to redraw a portion of the string, say
     *   the "CDE" part, to adjust the display for a visual change that
     *   doesn't affect character positioning - the user selected the text
     *   with the mouse, say.  In this case, the portable code would call
     *   measure_text() on the part *up to* the part we're redrawing - "AB" -
     *   to figure out where to position the part we *are* redrawing - "CDE".
     *   It would add the width returned from measure_text("AB") to the
     *   original position (0,0) to figure out where to draw "CDE".  So, it's
     *   important that the width returned from measure_text("AB") properly
     *   reflects the character positioning distance.  On most modern systems
     *   with proportional typefaces, the positioning width of a character
     *   often differs from its rendering width, because a glyph can overhang
     *   its positioning area and overlap the previous and next glyph areas.
     *   System APIs usually therefore offer separate measurement functions
     *   for the bounding box (the overall area occupied by all pixels in a
     *   string) and the positioning box (the area used to calculate where
     *   adjacent glyphs should be drawn).
     *   
     *   Note that the heights returned - both the returned y value giving
     *   the overall box height, as well as the ascent height - must be the
     *   DESIGN HEIGHTS for the font, NOT the actual height of the specific
     *   set of characters in 'str'.  The returned height is thus the
     *   *maximum* of the individual heights of *all* of the characters in
     *   the font.  This DOESN'T mean that a port has to actually run through
     *   all of the possible characters in a font and measure each one to
     *   find the largest value - instead, you should be able to call a
     *   simple OS API that gets this information directly.  Virtually every
     *   GUI font engine stores the design metrics as part of its internal
     *   data about a font, and makes them available to apps via a function
     *   that retrieves "font metrics" or something of the like.
     *   
     *   (The 'ascent height' is the distance between the TOP of the box
     *   containing any character in the font and the BASELINE.  The baseline
     *   is defined by the font's designer, but in general it's the alignment
     *   point for the bottoms of the capital letters and of the minuscules
     *   that don't have tails (tails: g, j, p, q, y - the bits that drop
     *   below the baseline).  The reason the ascent height is important is
     *   that it tells us where the baseline is relative to the overall
     *   rectangle containing a character in the font, and the baseline is
     *   important because it's the reference point for aligning adjacent
     *   text from different fonts.)  
     */
    virtual CHtmlPoint measure_text(class CHtmlSysFont *font,
                                    const textchar_t *str, size_t len,
                                    int *ascent) = 0;

    /*
     *   Get the size of the debugger source window control icon.  This is
     *   a small icon in the left margin of a line of text displayed in a
     *   debugger source window; the icon is meant to show the current
     *   status of the line (current line, breakpoint set on line, etc).
     *   This routine is only needed if the platform implements the TADS
     *   debugger; provide an empty implementation if you're not
     *   implementing the debugger on your platform. 
     */
    virtual CHtmlPoint measure_dbgsrc_icon() = 0;

    /*
     *   Determine how much of a given string can fit in a given width,
     *   assuming that the text is all on one line.  This routine is an
     *   optimization that allows an operating system mechanism to be used
     *   directly when we need to figure out how much text we can put on a
     *   line.  If the operating system doesn't provide a native mechanism
     *   for determining this, this routine should scan the string using the
     *   OS's text measuring mechanism.  In such cases, an efficient means
     *   should be used, such as a binary search through the string for the
     *   appropriate length.  If the entire string fits in the given width,
     *   the length of the string should be returned.
     *   
     *   The return value is expressed in byte units, even for multi-byte
     *   character sets.  The system implementation must take care to handle
     *   multi-byte characters properly, if the system supports MBCS's.  
     */
    virtual size_t get_max_chars_in_width(class CHtmlSysFont *font,
                                          const textchar_t *str,
                                          size_t len, long wid) = 0;

    /*
     *   Draw text.  The text is to be drawn with its upper left corner
     *   at the given position.  If hilite is true, the text should be
     *   drawn with selection highlighting; otherwise, it should be drawn
     *   normally.  
     */
    virtual void draw_text(int hilite, long x, long y,
                           class CHtmlSysFont *font,
                           const textchar_t *str, size_t len) = 0;

    /*
     *   Draw typographical text space.  This is related to draw_text(), but
     *   rather than drawing text, this simply draws a space of the given
     *   pixel width in the given font.  The effect should be as though
     *   draw_text() were called with a string consisting of a number of
     *   spaces equal in total horizontal extent to the given pixel width
     *   (note, however, that the width need not be an integral multiple of
     *   the width of a single ordinary space, so it's not possible in all
     *   cases to obtain the identical effect with draw_text).  This can be
     *   used for purposes such as drawing typographical spaces (spaces of
     *   particular special sizes), or inserting extra space for flush
     *   justification.
     *   
     *   Note that all of the same attributes of regular text drawing should
     *   apply.  Highlighting should work the same as with ordinary text,
     *   and if the font would draw ordinary space characters with any sort
     *   of decoration (such as underlines, strikethroughs, or overbars),
     *   the same decorations should be applied here.
     *   
     *   The simplest and safest implementation of this function is as
     *   follows.  Measure the size of an ordinary space in the given font.
     *   Start at the given x,y coordinates.  With clipping to the full
     *   height of the font and the given width, draw a space character.
     *   Move right by the width of the space.  Repeat until past the
     *   desired drawing width.  This approach will ensure that the space
     *   drawn will have exactly the same appearance as ordinary spaces; the
     *   iteration will ensure enough space is filled, and the clipping will
     *   ensure that there is no drawing beyond the desired space.  
     */
    virtual void draw_text_space(int hilite, long x, long y,
                                 class CHtmlSysFont *font, long wid) = 0;

    /* draw a bullet */
    virtual void draw_bullet(int hilite, long x, long y,
                             class CHtmlSysFont *font,
                             HTML_SysWin_Bullet_t style) = 0;

    /*
     *   Draw a horizontal rule covering the given rectangle.  If shade is
     *   true, draw with 3D-style shading, otherwise draw as a solid bar.
     */
    virtual void draw_hrule(const CHtmlRect *pos, int shade) = 0;

    /*
     *   Draw a table or cell border.  The border should be drawn inside
     *   the given rectangle with the given width.  If cell is true, this
     *   is an individual cell border, otherwise it's the overall table's
     *   border.  
     */
    virtual void draw_table_border(const CHtmlRect *pos, int width,
                                   int cell) = 0;

    /*
     *   Draw a table or cell's background.  This should simply fill in
     *   the given area with the given color. 
     */
    virtual void draw_table_bkg(const CHtmlRect *pos,
                                HTML_color_t bgcolor) = 0;

    /*
     *   Draw a debugger source line icon.  This is used only for the TADS
     *   debugger; if this platform doesn't implement the debugger, this
     *   routine can have an empty implementation.
     *   
     *   'stat' gives a status code; this is a combination of the
     *   HTMLTDB_STAT_xxx codes (see tadshtml.h).  
     */
    virtual void draw_dbgsrc_icon(const CHtmlRect *pos,
                                  unsigned int stat) = 0;


    /*
     *   Do formatting -- run the formatter until it runs out of content
     *   to format.  This routine can be implemented simply to call the
     *   formatter's formatting routine until the formatter reports that
     *   it's out of tags to format; however, we provide this method in
     *   the window class so that the window can optimize redrawing.  This
     *   routine should return true if it did any display updating, false
     *   if not.  If show_status is true, it indicates that the operation
     *   may be lengthy, so a status line message, wait cursor, and/or
     *   other indication of the busy status should be displayed;
     *   otherwise the operation is expected to finish quickly enough that
     *   no such display is desired.  show_status should be set to true
     *   when reformatting after operations that will reformat the entire
     *   window, such as resizing the window.  If update_win is true, we
     *   should update the window as soon as possible (i.e., as soon as
     *   we've formatted enough text to redraw the window); otherwise, we
     *   should leave redrawing to the normal draw message handler.  If
     *   freeze_display is true, the formatter should be told not to
     *   update the window until it's done; this is used when the whole
     *   window needs to be reformatted, such as after a font preference
     *   change, to avoid making the user watch the window's entire
     *   contents scroll by as the reformatting is done.
     */
    virtual int do_formatting(int show_status, int update_win,
                              int freeze_display) = 0;


    /* -------------------------------------------------------------------- */
    /*
     *   Palette management.  These routines are needed only if we're
     *   using an index-based palette to display colors, and then only if
     *   applications must manage the palette.  For systems with high
     *   color resolution (generally greater than 8-bit color) or
     *   automatic color table management, these routines can have empty
     *   implementations.  
     */
    
    /*
     *   Recalculate the palette.  The formatter calls this routine when
     *   it adds a new image to the list of initial images, which are used
     *   to determine the system palette.  If the system is not using a
     *   palette, this routine doesn't need to do anything.
     */
    virtual void recalc_palette() = 0;

    /*
     *   Determine if we need to use a palette.  If this system is
     *   currently using an index-based palette to select display colors,
     *   this should return true, otherwise it should return false.  Some
     *   systems use an index-based palette when the display hardware is
     *   showing 8 bits of color resolution or less (notably Windows).  
     */
    virtual int get_use_palette() = 0;

    /* -------------------------------------------------------------------- */
    /*
     *   Font management.  The window always owns all of the font
     *   objects, which means that it is responsible for tracking them and
     *   deleting them when the window is destroyed.  Fonts that are given
     *   to a formatter must remain valid as long as the window is in
     *   existence.  Note that this doesn't mean that the font class can't
     *   internally release system resources, if it's necessary on a given
     *   system to minimize the number of system font resources in use
     *   simultaneously -- the portable code can never access the actual
     *   system resources directly, so the implementation of this
     *   interface can manage the system resources internally as
     *   appropriate.  
     */
    
    /* get the default font */
    virtual class CHtmlSysFont *get_default_font() = 0;

    /* 
     *   Get a font suitable for the given characteristics.  Note that this
     *   routine is responsible for checking the font descriptor for the
     *   following parameterized names, and providing the appropriate actual
     *   system font when one of the parameterized names is used (the names
     *   are case-insensitive):
     *   
     *   TADS-serif - a serifed font, usually proportional
     *.  TADS-sans - a san serif font, usually proportional
     *.  TADS-script - a script or cursive font, usually proportional
     *.  TADS-typewriter - a typewriter-style font, usually fixed-width
     *.  TADS-input - the font to use for player commands
     *   
     *   When possible, the player should be able to select the parameterized
     *   fonts in a "preferences" dialog or similar mechanism.  The purpose
     *   of the parameterized fonts is to allow the game to specify the style
     *   to use, while letting the player specify the exact details of the
     *   presentation.
     *   
     *   Note that, on some systems, parameterized fonts might let the user
     *   specify attributes in addition to the font face.  For example, on
     *   the Windows interpreter, "TADS-Input" lets the user specify the text
     *   color and set boldface and italics.  When these additional
     *   attributes are part of a parameterized font name, they should be
     *   applied ONLY when the 'face_set_explicitly' flag in the font_desc is
     *   set to TRUE - this ensures that the attributes can be overridden by
     *   nesting other attribute tags (<font color=red> or <i>, for example)
     *   within the <font> tag that explicitly selects the font face.
     *   
     *   Note on memory management: the portable code never deletes a font
     *   object obtained from this routine.  Instead, this routine is
     *   expected to keep a list of font objects previously allocated, and to
     *   reuse an existing object if there's one in the list that matches the
     *   descriptor.  This ensures that the lack of deletion won't leak
     *   memory: a given font object is never deleted, but only one font
     *   object will ever be created for a given descriptor.  
     */
    virtual class CHtmlSysFont
        *get_font(const class CHtmlFontDesc *font_desc) = 0;

    /* 
     *   Get the font for drawing bullets, given a text font.  This can
     *   simply return the same font if there isn't a separate bullet font
     *   or bullets are drawn independently of the font.  The current font
     *   is provided so that a suitable size can be selected in the bullet
     *   font.  
     */
    virtual class CHtmlSysFont
        *get_bullet_font(class CHtmlSysFont *current_font) = 0;

    /* -------------------------------------------------------------------- */
    /*
     *   Timers
     */

    /*
     *   Register a timer callback routine.  This routine should be called
     *   with the given context data as its argument periodically.  If
     *   possible, the routine should be called about once per second, but
     *   the precise interval isn't important; all that's important is to
     *   call the function every so often so that the function can check
     *   for pending work.  Since the frequency of the callback invocation
     *   is not tightly specified, the callback must check the current
     *   time if it's time-sensitive.
     *   
     *   The callback should not be invoked via an interrupt or in a
     *   separate thread; instead, it should be invoked in the course of
     *   the window's normal event message dispatching mechanism.
     *   
     *   Note that any number of timer functions may be simultaneously
     *   registered.  The order in which such functions are called is
     *   unspecified.  
     */
    virtual void register_timer_func(void (*timer_func)(void *),
                                     void *func_ctx) = 0;

    /*
     *   Remove an timer callback function previously registered.  
     */
    virtual void unregister_timer_func(void (*timer_func)(void *),
                                       void *func_ctx) = 0;

    /*
     *   Create a timer object that calls a given callback function when the
     *   timer event occurs.  This doesn't register a timer, but merely
     *   creates a timer object that can be used in set_timer().
     *   
     *   Returns a CHtmlSysTimer object that can be used to unregister the
     *   timer.  
     */
    virtual class CHtmlSysTimer *create_timer(void (*timer_func)(void *),
                                              void *func_ctx) = 0;


    /*
     *   Set a timer, previously created with create_timer(), to the given
     *   interval in milliseconds.  If 'repeat' is true, we'll invoke the
     *   callback associated with the timer repeatedly at the given interval;
     *   otherwise, we'll invoke the callback just once and then make the
     *   timer inactive. 
     */
    virtual void set_timer(class CHtmlSysTimer *timer, long interval_ms,
                           int repeat) = 0;

    /*
     *   Cancel a timer. 
     */
    virtual void cancel_timer(class CHtmlSysTimer *timer) = 0;
    
    /*
     *   Delete a timer previously created with register_timer_func_ms().  If
     *   the timer is currently active, it is cancelled.  
     */
    virtual void delete_timer(class CHtmlSysTimer *timer) = 0;


    /* -------------------------------------------------------------------- */
    /*
     *   General HTML operations 
     */

    /*
     *   Adjust the horizontal scrollbar.  The formatter calls this whenever
     *   a new line is formatted that exceeds the maximum line width so far.
     *   This routine should adjust the horizontal scrollbar accordingly -
     *   that is, it should update the range (min/max) of the OS-level
     *   scrollbar control to match the new document width.
     *   
     *   Note that the routine should by default NOT perform any horizonal
     *   scrolling here - it should simply update the min/max RANGE of the
     *   scrollbar and leave the scrolling position unchanged.  However, if
     *   the window is a banner window with the style flag
     *   OS_BANNER_STYLE_AUTO_HSCROLL set (via create_banner_window() or
     *   set_banner_info()), then this routine should immediately scroll the
     *   window horizontally to bring the right edge of the document into
     *   view; the window should be invalidated and/or redrawn as needed, and
     *   the scrollbar's thumb position should be updated accordingly.  
     */
    virtual void fmt_adjust_hscroll() = 0;

    /*
     *   Adjust the vertical scrollbar.  The formatter calls this whenever a
     *   new line is added or the document otherwise grows vertically.  This
     *   routine should adjust the min/max range of the vertical scrollbar to
     *   match the new document height.
     *   
     *   Note that this routine should normally NOT perform any vertical
     *   scrolling - it should simply update the RANGE of the min/max
     *   scrollbar and leave the current scrolling position unchanged.
     *   However, if the window is a banner window with the style flag
     *   OS_BANNER_STYLE_AUTO_VSCROLL set (via create_banner_window() or
     *   set_banner_info()), then this routine should immediately scroll the
     *   window vertically to bring the bottom edge of the document into
     *   view; the window should be invalidated and/or redrawn as needed, and
     *   the scrollbar's thumb position should be updated accordingly.
     *   
     *   MORE MODE: We leave it up to the port code to determine how to
     *   handle MORE mode, if such a thing exists locally.  In a regular
     *   "main" window, or in a banner window with the style flag
     *   OS_BANNER_STYLE_MOREMODE set, the port should implement MORE mode
     *   according to local conventions.  One possible implementation is to
     *   do nothing special: since we presumably are showing the text in a
     *   standard GUI window with a scrollbar, any text that spills beyond
     *   the vertical extent of the window will simply be scrolled off the
     *   bottom, and we can leave it up to the user to manually use the
     *   scrollbar to see the text below the bottom of the window.  
     *   
     *   In most cases, though, it's best to simulate the behavior of the
     *   Unix "more" program, since (a) this is much more convenient for
     *   users, and (b) it's what most users are accustomed to in a
     *   terminal-style interface like ours.  Here's roughly how this works.
     *   Whenever fmt_adjust_vscroll() notifies the window that the document
     *   has grown vertically beyond the current vertical extent of the
     *   window, the window sets an internal flag saying "more mode is
     *   pending."  Whenever the program makes an input call (get_input(),
     *   get_input_timeout(), etc), the window checks the "more mode pending"
     *   flag, and if set, clears that flag and sets another one saying "more
     *   mode is active."  In the input routines, as long as the "more mode"
     *   flag is set, we treat keyboard input specially: the SPACE key
     *   scrolls down by a page, the RETURN key scrolls down by a "line"
     *   (which is an essentially arbitrary unit, since we can have a mixture
     *   of text heights in the window - the design height of the default or
     *   current font is usually a good choice), the up/down arrow keys
     *   scroll by a line one way or the other; assign other keys according
     *   to local conventions or your preferences, but generally ignore keys
     *   to which you haven't assigned a special more-mode meaning.  When in
     *   more mode, if at any time the vertical scroll position becomes such
     *   that the bottom of the document is in view, cancel more mode and
     *   switch to ordinary input mode.  In ordinary input mode, you'd
     *   normally use a different scrolling behavior: if the input caret
     *   isn't visible, and the user presses any key that affects the input,
     *   immediately jump directly to a scrolling position that brings the
     *   caret into view.  This allows the user to scroll up to look at old
     *   text in the transcript, but then immediately jump back to the
     *   command line just by starting to type some input.
     *   
     *   We leave all of this up to the port code, though, because we want to
     *   give each port maximum flexibility in applying local customs.  Some
     *   ports might not even want a MORE mode at all, and some might use
     *   different key mappings or different overall behaviors.  Some ports
     *   might provide a visual indication that MORE mode is active
     *   (something like the "[More]" prompt that the Unix "more" command and
     *   the TADS text-only interpreters display), while some might be
     *   content to rely on the scrollbars as a sufficient visual cue.  The
     *   Windows interpreter uses something like the traditional "[More]"
     *   prompt, but it has a couple of user-configurable options for how to
     *   display it: it can be shown in the status bar along the bottom of
     *   the window frame, or it can be displayed as an overlay within the
     *   document area of the window itself, superimposed over the document's
     *   own contents at the bottom of the window.  (The status line approach
     *   is a lot easier to implement.)  
     */
    virtual void fmt_adjust_vscroll() = 0;

    /*
     *   Invalidate the window area given by the document coordinates (i.e.,
     *   before taking into account any scrolling).
     *   
     *   Note that the area->right and area->bottom coordinates can be passed
     *   in as HTMLSYSWIN_MAX_RIGHT and/or HTMLSYSWIN_MAX_BOTTOM, defined
     *   above - these indicate that you should invalidate all the way to the
     *   right or bottom edge of the window.
     */
    virtual void inval_doc_coords(const class CHtmlRect *area) = 0;

    /*
     *   Receive notification that the formatter associated with this
     *   window is about to reset the display list, deleting all existing
     *   display list items.  Any display list items that the window is
     *   referencing should be forgotten.  It's particularly likely that
     *   the window is keeping track of a link display item, such as the
     *   link that the mouse is hovering over.  The window should simply
     *   forget about any such display list items at this time.  
     */
    virtual void advise_clearing_disp_list() = 0;

    /*
     *   Scroll a document position into view.  This should try to scroll
     *   the window so that the given coordinates appear at the middle of
     *   the window.  If possible, the entire rectangle should be within
     *   the window.  
     */
    virtual void scroll_to_doc_coords(const class CHtmlRect *pos) = 0;

    /*
     *   Get the current scrolling position in document coordinates.
     *   Returns a value suitable for passing to scroll_to_doc_coords to
     *   restore this same scrolling position at a later time.  
     */
    virtual void get_scroll_doc_coords(class CHtmlRect *pos) = 0;

    /* set the window's title */
    virtual void set_window_title(const textchar_t *title) = 0;

    /*
     *   Set the background color, text color, and link colors.  If
     *   use_default for a particular color is true, we'll ignore the
     *   provided color and use the default color instead.  These calls are
     *   used to advise the system window implementation of the color
     *   settings made programmatically, such as through the <BODY> tag.
     *   
     *   The "background" color is the color used to fill the background of
     *   the window, where no text or graphics are explicitly drawn.
     *   
     *   The "text" color is the default color for ordinary text (i.e., text
     *   that isn't hyperlinked or given an explicit font color via <FONT
     *   COLOR=xxx> or the like).
     *   
     *   The "input" color is the color to use for input text.
     *   
     *   The "link" color is the color for hyperlinked text - that is, text
     *   inside an <A HREF> tag.
     *   
     *   The "vlink" color is the "visited" hyperlink text color.  We include
     *   this for completeness, but currently we don't use it, since we don't
     *   really have a sense of visited vs unvisited links in the same way
     *   that a browser would.
     *   
     *   The "alink" color is the color for "active" hyperlink text.  This is
     *   the color for hyperlink text when the link is being clicked by the
     *   mouse; that is, when the user has moved the mouse over the hyperlink
     *   and then clicked and held down the button.  This is the color to use
     *   as long as the button is being held down.
     *   
     *   The "hlink" color is the "hover" hyperlink text color.  This is the
     *   color for when the mouse is hovering over the hyperlink, but the
     *   mouse button isn't being held down.
     *   
     *   Note that the system implementation is free to ignore all of these
     *   color settings, or to use them only if user preferences allow, or
     *   under any other conditions suitable for the local system.  On most
     *   systems, there are four possible color sources for any given text
     *   fragment: user preferences; set_html_xxx_color() settings; explicit
     *   <FONT> and other settings; and OS defaults.  The interpreter then
     *   implements a hierarchy to determine which of these to use.  The
     *   exact hierarchy is up to the interpreter, but it's usually like
     *   this:
     *   
     *   - If the user preferences allow the user to select an "override"
     *   option, and the user selects this option, use the user preference
     *   color setting applicable to a given text fragment.  (The user
     *   preferences might allow the user to select each of these color types
     *   individually - text, background, input, alink, vlink, etc - in which
     *   case the interpreter should pick the right one for the context.  If
     *   the preferences don't distinguish color by context, just use the
     *   single color setting.)
     *   
     *   - If "override" isn't selected (or isn't offered), and there's a
     *   <FONT COLOR=xxx> setting for a given text fragment, use that.
     *   
     *   - Otherwise, if there's been a call to set_html_xxx_color() for the
     *   given context with use_default=FALSE, use that color.
     *   
     *   - If there's been no call to set_html_xxx_color() for the given
     *   context, or there's been a call with use_default=TRUE, AND there's a
     *   user preference setting applicable to the context, use the user
     *   preference setting.
     *   
     *   - Otherwise, use the system defaults.  
     */
    virtual void set_html_bg_color(HTML_color_t color, int use_default) = 0;
    virtual void set_html_text_color(HTML_color_t color, int use_default) = 0;
    virtual void set_html_input_color(HTML_color_t clr, int use_default) = 0;
    virtual void set_html_link_colors(HTML_color_t link_color,
                                      int link_use_default,
                                      HTML_color_t vlink_color,
                                      int vlink_use_default,
                                      HTML_color_t alink_color,
                                      int alink_use_default,
                                      HTML_color_t hlink_color,
                                      int hlink_use_default) = 0;

    /*
     *   Map a special parameterized color value to the corresponding RGB
     *   value.  The parameterized color values are the HTML_COLOR_xxx
     *   values defined in tadshtml.h.  The system should use an
     *   appropriate mechanism for determining the colors; the system may
     *   use fixed values, or may choose colors appropriate for the
     *   current display device, or can let the user control these
     *   settings through a preferences mechanism. 
     */
    virtual HTML_color_t map_system_color(HTML_color_t color) = 0;

    /* 
     *   Get the link colors.  The link colors are stored in the window
     *   object, so that the system-specific code can save, use, and
     *   customize user preferences as needed.  The colors are normal (link),
     *   activated/clicked (alink), visited (vlink), and hovering (hlink).
     *   These are the colors that are used to draw hyperlinked text in the
     *   respective states.
     *   
     *   Note that these should return the colors appropriate for the current
     *   system-specific preference settings.  For example, if the user
     *   preferences the user to select whether or not links are highlighted
     *   on "hovering" with the mouse cursor, and hover highlighting is
     *   currently turned off, then the hlink color should return the same as
     *   the normal link color.  
     */
    virtual HTML_color_t get_html_link_color() const = 0;
    virtual HTML_color_t get_html_alink_color() const = 0;
    virtual HTML_color_t get_html_vlink_color() const = 0;
    virtual HTML_color_t get_html_hlink_color() const = 0;

    /* determine if textual links should be drawn underlined */
    virtual int get_html_link_underline() const = 0;

    /*
     *   Determine if links are to be shown.  This should be offered as a
     *   user-settable preference if possible.  Returns true if links
     *   should be shown as links, false if not.  If links are not to be
     *   shown as links, we'll draw links as ordinary text.  
     */
    virtual int get_html_show_links() const = 0;

    /*
     *   Determine if graphics can be displayed.  On systems where
     *   graphics can be displayed, this should be offered as a
     *   user-settable preference if possible.  On systems where graphics
     *   can't be displayed at all, this should always return false.  If
     *   this returns false, we'll render graphics using a suitable
     *   textual substitute if possible, such as the ALT attribute of an
     *   IMG tag, or simply leave any graphics out entirely.  If this
     *   returns true, we'll render graphics as normal.  
     */
    virtual int get_html_show_graphics() const = 0;

    /*
     *   Set the background image.  The image is an ordinary HTML resource
     *   cache entry, whose get_image() method will return a CHtmlSysImage
     *   object (or null if there's not a valid object associated with the
     *   cache entry).
     *   
     *   In graphical implementations, the background image is normally drawn
     *   as the background of the document, tiled to fill out the window
     *   space.  The image should scroll with the document.  As the document
     *   scrolls down, the image is normally repeatedly tiled vertically to
     *   fill in all displayed space.  The image is normally drawn out to the
     *   visible edges of the window; no empty margin space is normally shown
     *   between the interior edges of the window's frame and the image.  
     */
    virtual void set_html_bg_image(class CHtmlResCacheObject *image) = 0;

    /*
     *   Invalidate a portion of the background image.  This is called when
     *   the background image is animated to trigger redrawing of the
     *   portions of the image changed from one animation frame to the next.
     *   
     *   The coordinates given are relative to the upper left corner of the
     *   background image itself.  These are not in document or window
     *   coordinates - they're in *image* coordinates.  The portable caller
     *   can't make any assumptions about the placement of the background
     *   image relative to the window or document coordinate systems, so we
     *   must rely on the window itself to figure out what needs to be
     *   invalidated within the image.
     *   
     *   It's important to note that if the image is tiled, so that it's
     *   drawn multiple times in the window, then *each visible tile* must be
     *   invalidated here.  If all of the visible tiles are not invalidated,
     *   they would incorrectly remain un-updated - but they'd draw in their
     *   new form eventually when the window is next updated by virtue of
     *   having a portion uncovered.  Therefore, it's crucial that this
     *   routine properly invalidate all visible copies of the image.  
     */
    virtual void inval_html_bg_image(unsigned int x, unsigned int y,
                                     unsigned int wid, unsigned int ht) = 0;

    /*
     *   Set the size of this window as a banner.  A call to this routine
     *   should be ignored for a main window.
     *   
     *   Note that normally only one of the dimensions is actually used.  If
     *   this is a horizontal banner, normally only the height is meaningful,
     *   because horizontal banners are constrained to run across the entire
     *   width of the main window.  Similarly, only the width is meaningful
     *   for vertical banners.
     *   
     *   If use_height is false, it means that the height parameter value
     *   should be ignored and the current window height retained; likewise
     *   use_width.  
     */
    virtual void set_banner_size(
        long width, HTML_BannerWin_Units_t width_units, int use_width,
        long height, HTML_BannerWin_Units_t height_units, int use_height) = 0;

    /*
     *   Set alignment and style for this existing banner window.  A call to
     *   this routine should be ignored for a main window.
     *   
     *   'style' is a combination of OS_BANNER_STYLE_xxx flags, as defined
     *   in osifc.h (from tads 2).  
     */
    virtual void set_banner_info(HTML_BannerWin_Pos_t pos,
                                 unsigned long style) = 0;

    /*
     *   Get information on this banner window.  '*pos' is filled in with the
     *   banner's alignment, and '*style' is filed in with a combination of
     *   OS_BANNER_STYLE_xxx flags describing the styles actually provided by
     *   the window.
     *   
     *   Note that the style flags returned might not be the same ones as
     *   originally requested in CHtmlSysFrame::create_banner_window(),
     *   because we might not support some styles that the caller requested,
     *   and we might unconditionally use some styles not requested.  The
     *   style flags returned here describe the *actual* window, not the
     *   requested one.  
     */
    virtual void get_banner_info(HTML_BannerWin_Pos_t *pos,
                                 unsigned long *style) = 0;


protected:
    /* formatter for the window */
    class CHtmlFormatter *formatter_;
};

/* ------------------------------------------------------------------------ */
/*
 *   Timer.  This is an opaque object for the system window implementation's
 *   use.
 */
class CHtmlSysTimer
{
public:
    CHtmlSysTimer(void (*func)(void *), void *ctx)
    {
        /* remember the function and its context */
        func_ = func;
        func_ctx_ = ctx;

        /* presume we won't repeat */
        repeat_ = FALSE;

        /* we're not yet active */
        active_ = FALSE;
    }

    /* set repeat mode */
    void set_repeating(int repeat) { repeat_ = repeat; }

    /* am I a repeating timer? */
    int is_repeating() const { return repeat_; }

    /* invoke my function */
    void invoke_callback() { (*func_)(func_ctx_); }

    /* get/set active status */
    int is_active() const { return active_; }
    void set_active(int active) { active_ = active; }

protected:
    /* function (and its context) to call upon timer expiration */
    void (*func_)(void *ctx);
    void *func_ctx_;

    /* flag: the timer is active */
    int active_;

    /* flag: the timer is to be repeated (as opposed to called just once) */
    int repeat_;
};

/* ------------------------------------------------------------------------ */
/*
 *   System resource object interface.  This is the base type for resources,
 *   such as sounds and images.  This interface is subclassed to provide the
 *   specific resource types; the subclassed interfaces must in turn be
 *   implemented by the non-portable system-dependent code.  This interface
 *   itself is not to be implemented by system code -- only the final
 *   subclasses of this interface need to be implemented.
 *   
 *   We generally deal with resources at a generic level, since we can't tell
 *   the type of an image until we've loaded it.  However, when we use a
 *   resource, we generally need to downcast the resource to a particular
 *   "branch" of the resource taxonomy based on context; for example, when
 *   we're attempting to draw a resource loaded from an <IMG> tag, we'll need
 *   a CHtmlSysImage object.  To accommodate the need to downcast resources
 *   with type safety, this interface provides virtuals that cast to the
 *   major resource type branches.  Note that it should never be necessary to
 *   downcast beyond the immediate subclasses of this interface, since any
 *   further subclasses should be invisible to client code (for example,
 *   CHtmlSysImage is sufficiently polymorphic that client code can do
 *   everything it needs to do to an image without having to worry about
 *   whether it's a PNG or JPEG image).  The downcast methods return null for
 *   resources that are not of the requested type.  
 */
class CHtmlSysResource
{
public:
    virtual ~CHtmlSysResource() { }

    /* get the resource type */
    virtual HTML_res_type_t get_res_type() const = 0;
    
    /* downcasts to major resource types */
    virtual class CHtmlSysImage *cast_image() { return 0; }
    virtual class CHtmlSysSound *cast_sound() { return 0; }

    /* 
     *   Can the resource be kept in the resource cache and reused?  This is
     *   true for stateless resources, such as static pictures, and false for
     *   stateful resources, such as animated images (MNG's, for example).
     *   Stateful resources cannot be reused if they appear multiple times in
     *   a document because each copy in the document must have a separate
     *   resource instance to manage its state.  
     */
    virtual int is_cacheable() const { return TRUE; }
};

/* ------------------------------------------------------------------------ */
/* 
 *   drawing modes - these specify how to draw an image into a rectangle
 *   whose size differs from the size of the image 
 */
enum htmlimg_draw_mode_t
{
    /* 
     *   Draw the image at its exact size, aligning the upper left corner of
     *   the image at the upper left corner of the drawing rectangle.  Clip
     *   the image at the right and bottom to fit the drawing area if the
     *   image is too big.  Leave any excess right and bottom margin
     *   unaffected if the image is smaller than the drawing area.  
     */
    HTMLIMG_DRAW_CLIP,

    /*
     *   Stretch the image to fit the drawing area, by expanding or shrinking
     *   the image as needed.  The image should be scaled as visually
     *   smoothly as possible.  
     */
    HTMLIMG_DRAW_STRETCH,

    /*
     *   Tile the image to fit the bounding area, starting at the upper left
     *   corner and drawing it repeatedly without any space between adjacent
     *   tiles.  If the image is too large to fit the area, clip it to fit,
     *   aligning the upper left corner of the image at the upper left
     *   corner of the target rectangle.  
     */
    HTMLIMG_DRAW_TILE
};

/* ------------------------------------------------------------------------ */
/*
 *   System image object.  This type of object allows us to display an
 *   image.  This interface must be subclassed for each image type (JPEG,
 *   etc).  This interface is not to be implemented by system code; only
 *   the final subclasses that provide interfaces for the specific image
 *   types need to be implemented.  
 */
class CHtmlSysImage: public CHtmlSysResource
{
public:
    virtual ~CHtmlSysImage() { }

    /* this is an image - override the downcast method */
    CHtmlSysImage *cast_image() { return this; }

    /* cast to an animated image */
    virtual class CHtmlSysImageAnimated *cast_animated() { return 0; }

    /*
     *   Set the display site.  This is REQUIRED for animated images.  For
     *   non-animated images, this has no effect, but as a convenience, we
     *   define the method as a virtual in the base image class so that an
     *   image container can simply always set the site if it provides one.
     */
    virtual void set_display_site(class CHtmlSysImageDisplaySite *) { }

    /*
     *   Cancel playback.  If the image is animated, this should halt the
     *   animation.  This has no effect for static images.
     */
    virtual void cancel_playback() { }

    /*
     *   Pause/resume playback.  If the image is animated, this should pause
     *   playback until 'resume' is called.  This has no effect for static
     *   images.  
     */
    virtual void pause_playback() { }
    virtual void resume_playback() { }
    
    /* 
     *   Display the image at a given position in a given window.  The 'mode'
     *   argument indicates what to do if the size of the image doesn't
     *   exactly match the size of the rectangle we're drawing into: we can
     *   clip, stretch, or tile the image.  
     */
    virtual void draw_image(class CHtmlSysWin *win, class CHtmlRect *pos,
                            htmlimg_draw_mode_t mode) = 0;

    /* get the image's dimensions */
    virtual unsigned long get_width() const = 0;
    virtual unsigned long get_height() const = 0;

    /*
     *   Map the image's palette.  If 'foreground' is true, the image's
     *   palette should take over the system palette, if possible;
     *   otherwise, the image's palette should be mapped as well as
     *   possible into the existing system palette.  Returns true if
     *   anything changed in the system palette, false if not.  For
     *   systems that don't use palettes, this routine doesn't need to do
     *   anything.  
     */
    virtual int map_palette(class CHtmlSysWin *win, int foreground) = 0;
};

/*
 *   System JPEG image object. 
 */
class CHtmlSysImageJpeg: public CHtmlSysImage
{
public:
    virtual HTML_res_type_t get_res_type() const
        { return HTML_res_type_JPEG; }

    /*
     *   Create a new system-specific JPEG object, loading the JPEG data
     *   from the given seek offset in the given file.  Note that this is
     *   a static routine that must be implemented for this class in a
     *   system-specific module.  This routine should return a new
     *   instance of the correct subclass of this class for the current
     *   operating system.
     *   
     *   Note that 'filesize' is the size of the data starting at
     *   'seekpos' that the resource loader considers to be part of this
     *   resource; the actual file may be larger than this value, since
     *   other data may follow the resource in the file.
     *   
     *   'url' is provided for diagnostic purposes; it should be used in
     *   any error message that this routine generates (with
     *   oshtml_dbg_printf).  It can otherwise be ignored.
     *   
     *   The 'win' argument is provided so that the loader can check the
     *   current environment for any information necessary for loading the
     *   resource.  For example, the loader may want to call
     *   win->get_use_palette() to determine if the image's color map must
     *   be mapped to a palette.  
     */
    static CHtmlSysResource *create_jpeg(const class CHtmlUrl *url,
                                         const textchar_t *filename,
                                         unsigned long seekpos,
                                         unsigned long filesize,
                                         class CHtmlSysWin *win);
};

/*
 *   System PNG image object 
 */
class CHtmlSysImagePng: public CHtmlSysImage
{
public:
    virtual HTML_res_type_t get_res_type() const
        { return HTML_res_type_PNG; }

    /*
     *   Create a new system-specific PNG object, loading the PNG data
     *   from the given seek offset in the given file.  Note that this is
     *   a static routine that must be implemented for this class in a
     *   system-specific module.  This routine should return a new
     *   instance of the correct subclass of this class for the current
     *   operating system.
     *   
     *   Note that 'filesize' is the size of the data starting at
     *   'seekpos' that the resource loader considers to be part of this
     *   resource; the actual file may be larger than this value, since
     *   other data may follow the resource in the file.
     *   
     *   'url' is provided for diagnostic purposes; it should be used in
     *   any error message that this routine generates (with
     *   oshtml_dbg_printf).  It can otherwise be ignored.
     *   
     *   The 'win' argument is provided so that the loader can check the
     *   current environment for any information necessary for loading the
     *   resource.  For example, the loader may want to call
     *   win->get_use_palette() to determine if the image's color map must
     *   be mapped to a palette.  
     */
    static CHtmlSysResource *create_png(const class CHtmlUrl *url,
                                        const textchar_t *filename,
                                        unsigned long seekpos,
                                        unsigned long filesize,
                                        class CHtmlSysWin *win);
};

/*
 *   System Animated Image object.  This subclass of image is for animated
 *   image types; these objects require special interaction with their
 *   display site to manage timed playback.  
 */
class CHtmlSysImageAnimated: public CHtmlSysImage
{
public:
    /* cast to an animated image */
    virtual class CHtmlSysImageAnimated *cast_animated() { return this; }

    /* 
     *   Animated images are all non-shareable, because an animated image
     *   resource must keep track of state information (i.e., where it is in
     *   displaying the animation).  
     */
    virtual int is_cacheable() const { return FALSE; }

    /*
     *   Receive notification from the display site of a timer event.  The
     *   display site must call this method after a given delay has occurred
     *   after a request from the image made to the display site via the
     *   display site interface (CHtmlSysImageDisplaySite).  
     */
    virtual void notify_timer() = 0;

    /*
     *   Receive notification from the image helper (if we use one) that the
     *   image has changed.  This doesn't necessarily need to be implemented:
     *   it's only needed if the system implementation of this class uses a
     *   portable helper object, and the helper object can change the image
     *   in response to a timer, and the system implementation of this class
     *   has its own separate buffer that it must refresh from the helper
     *   object in such cases. 
     */
    virtual void notify_image_change(int x, int y, int wid, int ht) = 0;
};

/*
 *   System MNG image object 
 */
class CHtmlSysImageMng: public CHtmlSysImageAnimated
{
public:
    virtual HTML_res_type_t get_res_type() const
        { return HTML_res_type_MNG; }

    /*
     *   Create a new system-specific MNG object, loading the MNG data from
     *   the given seek offset in the given file.  Note that this is a static
     *   routine that must be implemented for this class in a system-specific
     *   module.  This routine should return a new instance of the correct
     *   subclass of this class for the current operating system.
     *   
     *   Note that 'filesize' is the size of the data starting at 'seekpos'
     *   that the resource loader considers to be part of this resource; the
     *   actual file may be larger than this value, since other data may
     *   follow the resource in the file.
     *   
     *   'url' is provided for diagnostic purposes; it should be used in any
     *   error message that this routine generates (with oshtml_dbg_printf).
     *   It can otherwise be ignored.
     *   
     *   The 'win' argument is provided so that the loader can check the
     *   current environment for any information necessary for loading the
     *   resource.
     */
    static CHtmlSysResource *create_mng(const class CHtmlUrl *url,
                                        const textchar_t *filename,
                                        unsigned long seekpos,
                                        unsigned long filesize,
                                        class CHtmlSysWin *win);
};

/* ------------------------------------------------------------------------ */
/*
 *   Display site interface.  Unlike static images, where the display site
 *   runs the show and always calls the image to cause a display, animated
 *   images are active, so they have to be able to notify the display site
 *   whenever an animation event occurs.  This interface provides a means for
 *   an animated image object to notify the window (or object within a
 *   window) where the animation is displayed when events occur.
 *   
 *   Since this is an abstract interface, animated images not bound to any
 *   particular type of display container; any class that wants to display an
 *   animated image resource can implement this interface.  
 */
class CHtmlSysImageDisplaySite
{
public:
    virtual ~CHtmlSysImageDisplaySite() {}

    /*
     *   Invalidate a portion of the display site: the MNG image's contents
     *   have changed (due to animation playback) within the invalidated
     *   area, so the area should be redisplayed as soon as possible.  The
     *   area to be invalidated is given relative to the upper left corner of
     *   the display site.  
     */
    virtual void dispsite_inval(unsigned int x, unsigned int y,
                                unsigned int width, unsigned int height) = 0;

    /*
     *   Set a timer event.  The site must call the image object's
     *   notify_timer() method as soon as possible after the given real-time
     *   interval (in milliseconds) elapses.
     *   
     *   We will request only one timer at any given time.  If there's still
     *   a timer request outstanding when we make a new timer request, the
     *   new request should REPLACE the old request.  
     */
    virtual void dispsite_set_timer(unsigned long delay_ms,
                                    class CHtmlSysImageAnimated *image) = 0;

    /*
     *   Cancel the timer.  Any pending timer request should be cancelled.
     *   No further calls to notify_timer() in the image object are desired.
     */
    virtual void dispsite_cancel_timer(class CHtmlSysImageAnimated *) = 0;
};


/* ------------------------------------------------------------------------ */
/*
 *   System sound object.  This type of object allows us to play back a
 *   sound.  This interface must be subclasses for each sound type (MIDI,
 *   WAV).  This interface is not to be implemented by system code; only
 *   the final subclasses that provide interfaces for the specific sound
 *   types need to be implemented.  
 */
class CHtmlSysSound: public CHtmlSysResource
{
public:
    virtual ~CHtmlSysSound() { }

    /* this is a sound - override the downcast method */
    CHtmlSysSound *cast_sound() { return this; }

    /* 
     *   sounds are not cacheable, because sound resources must keep track of
     *   where they are in the playback 
     */
    virtual int is_cacheable() const { return FALSE; }

    /*
     *   Start playing the sound.  When the sound is finished, invoke the
     *   given callback (which can be null, to indicate that no callback is
     *   to be invoked) with the given context.  The 'repeat_count' parameter
     *   to the callback indicates how many times the sound has been played.
     *   
     *   'repeat' is the number of times to play back the sound; 0 indicates
     *   that the sound should be played forever (or until cancel_sound is
     *   called, whichever comes first).  The player can ignore the repeat
     *   count, however (this is to simplify the implementation when it's not
     *   convenient or useful to implement repetition in the player itself).
     *   If the repeat count is obeyed, the callback will be invoked after
     *   all iterations have been performed, and will indicate the number of
     *   iterations completed.  If the repeat count is ignored, the callback
     *   will be invoked after the single iteration we'll perform and will
     *   indicate that only one iteration has been played.  In this case,
     *   caller can simply requeue the sound (if still desired) with the next
     *   lower repeat count.
     *   
     *   'vol' is the base playback volume for the track, in the range
     *   0..100, where 0 is silence and 100 is unattenuated (i.e., play the
     *   track at the full volume level of the recorded data).  Values
     *   outside this range are invalid and should be pegged to the range
     *   (i.e., treat anything below 0 as 0, and anything over 100 as 100).
     *   This volume level is NOT a master volume for the whole computer or
     *   whole application - it shouldn't affect any other sound levels in
     *   the system or be used to adjust the physical speaker volume.  This
     *   parameter instead simply sets the relative playback volume of this
     *   track.  A volume level of 0 means the track should effectively be
     *   muted; 100 means it should be played back unattenuated, at the full
     *   native volume recorded in the sound file.  Values between 0 and 100
     *   should be perceptually linear volume levels, akin to adjusting a
     *   speaker volume knob between minimum and maximum volume.  If the
     *   local system can't implement per-track relative volume control,
     *   simply ignore this parameter and play back the track at its native
     *   volume level.  (Although you might still want to treat volume 0 as
     *   mute in this case, if possible.)
     *   
     *   'fade_in' and 'fade_out' are the start-of-track and end-of-track
     *   fade times, in milliseconds.  Zero means that the track should
     *   start/end at full volume.  If a non-zero fade time is given, it
     *   means that the volume on the track should fade in or out over the
     *   given interval - fade_in is the interval for fading in at the start
     *   of the track, and fade_out is the interval for fading out at the end
     *   of the track.  This is a "best effort" feature - if fades can't be
     *   implemented on the platform, or can't be implemented for certain
     *   audio formats, simply ignore the fade parameters and start/stop at
     *   full volume.
     *   
     *   'crossfade' indicates whether the end-of-track fade-out should be a
     *   cross-fade or a regular fade.  True means cross-fade: as soon as the
     *   fade-out begins, call the 'done' callback, which will tell the queue
     *   manager that it can start playing the next track in the queue
     *   immediately, even while the fade is proceeding.  False means a
     *   regular fade-out: the 'done' callback should be invoked after the
     *   track is fully finished (including the fade), as normal.  If
     *   cross-fades can't be implemented on the platform, ignore this
     *   parameter and do a normal fade-out.
     *   
     *   Note that 'crossfade' doesn't affect the fade-in.  Any cross-fade at
     *   fade-in is controlled by the *outgoing* track - it's up to the
     *   previous track to determine when to call the 'done' callback to
     *   enable to next queued track to begin playing.
     *   
     *   When the repeat count is 0 or >1, the fade-in should be applied only
     *   to the first iteration, and the fade-out should be applied only to
     *   the last iteration.  There should be no fade between repeated
     *   iterations of the same track.
     *   
     *   Returns zero on success, non-zero on error.  If a non-zero value is
     *   returned, the callback is NOT invoked.
     *   
     *   The URL is provided for diagnostic purposes.  If the routine
     *   encounters an error and wants to provide details, it can use the URL
     *   in a debugging message to indicate the object that caused the error.
     */
    virtual int play_sound(class CHtmlSysWin *win,
                           void (*done_func)(void *, int repeat_count),
                           void *done_func_ctx, int repeat,
                           const textchar_t *url, int vol,
                           long fade_in, long fade_out, int crossfade) = 0;

    /*
     *   Add an end-of-track cross-fade to this track, if it's currently
     *   playing.  This has no effect if the track isn't already playing or
     *   has finished playing.
     *   
     *   'ms' is the duration of the fade, in milliseconds.  The fade should
     *   begin 'ms' milliseconds before the end of the track; the volume
     *   level should be gradually decreased over that interval from the
     *   normal playback volume to silence.
     *   
     *   The point of this routine is to allow an *incoming* track to ask for
     *   a crossfade as it starts.  The request can't be made as part of the
     *   play_sound() on the incoming track - not just because play_sound()
     *   doesn't offer a way to do this, but because there's no way for
     *   play_sound() to offer a way to do this even if it wanted to.  A
     *   cross-fade is always a feature of an outgoing track, because the way
     *   you accomplish a cross-fade is to have the outgoing track signal
     *   that it's finished (by calling the 'done' callback) at the start of
     *   its fade-out, to let the next queued track begin playing.  So, the
     *   way to handle an incoming cross-fade is to queue the incoming sound,
     *   and call this routine on the immediately preceding sound in the
     *   queue.
     *   
     *   Note that there might already be a scheduled fade-out on this track,
     *   because play_sound() can request a cross-fade as well (and because
     *   this routine could conceivably be called more than once for a single
     *   track).  If there are multiple fade-outs for the same track, the
     *   recommended handling is to combine them by always setting the volume
     *   level to the MININUM of the volume levels from the competing fades.
     *   This will ensure a monotonic fade - the volume level won't jump
     *   around, because it will only be able to decrease over time.  The
     *   volume level might not decrease in a simple straight line (it could
     *   be a series of linear segments, because the different fade slopes
     *   might intersect), but this really shouldn't matter perceptually;
     *   most listeners probably won't even notice that the fade isn't
     *   perfectly linear as long as it's monotonic.
     *   
     *   If cross-fades can't be implemented on this platform, or it's not
     *   possible to add a cross-fade to a track already being played, simply
     *   ignore calls to this routine.  
     */
    virtual void add_crossfade(class CHtmlSysWin *win, long ms) = 0;

    /*
     *   Cancel the sound.
     *   
     *   If 'sync' is true, the sound must be canceled, and the callback
     *   invoked, BEFORE this routine returns.  If 'sync' is false, this
     *   routine MAY return before the sound has actually stopped, as long as
     *   the sound is scheduled for cancellation very shortly.  (Of course,
     *   the callback should still be invoked in this case, as soon as the
     *   sound actually stops playing.)
     *   
     *   (The point os 'sync' is to allow for situations where the playback
     *   is handled by a background thread, hardware sound card buffers, or
     *   another asynchronous player mechanism.  For example, if the playback
     *   is being handled by a background thread, when 'sync' is false this
     *   routine could simply post a message to the playback thread telling
     *   it to stop, then immediately return without waiting for the message
     *   to be received.  Playback might continue in this case for a few
     *   milliseconds, until the OS gets around to scheduling the playback
     *   thread for its next chance to run.  This type of approach helps keep
     *   the UI responsive by avoiding blocking waits for playback to catch
     *   up with UI activity.)
     *   
     *   If 'sync' is true OR 'fade_out_ms' is zero, the cancellation is
     *   abrupt - the sound should simply be cut off in mid-stream.  If a
     *   callback was registered when the sound was started, the callback
     *   should be invoked as though the track had reached its normal ending,
     *   as soon as the playback actually terminates.
     *   
     *   If 'fade_out_ms' is non-zero, it specifies a number of milliseconds
     *   over which the sound should gradually fade out.  The fade should
     *   start immediately; the volume of the playback for this sound (and
     *   ONLY this sound) should be reduced gradually from full volume to
     *   silence, over the course of the specified interval.
     *   
     *   If a non-zero fade interval is specified, the 'fade_in_bg' flag
     *   specifies how the callback function (if one was specified in
     *   play_sound) should be invoked.  If 'fade_in_bg' is true, it means
     *   that the callback should be invoked immediately, before the function
     *   returns.  Otherwise, the callback should be invoked as soon as the
     *   fade is finished.
     *   
     *   Note that regardless of the 'fade_in_bg' setting, the fade itself
     *   should happen "in the background" with respect to the UI, if
     *   possible.  That is, this routine should schedule the fade to begin
     *   immediately, and should then immediately return so that the UI
     *   remains responsive during the fade interval.  This routine should
     *   NOT wait until after the fade is finished to return to its caller.
     *   
     *   (The point of setting 'fade_in_bg' to true is to allow for
     *   cross-fade effects: since the callback is invoked immediately, the
     *   next sound in the same queue ("layer") will be able to start playing
     *   immediately, so the new sound will play concurrently while the old
     *   sound fades out.  Using a fade-in with the new sound will
     *   effectively cross-fade the two sounds, creating a smooth transition
     *   between adjacent tracks.)
     *   
     *   Synchronous mode and fade mode are mutually exclusive.  If 'sync' is
     *   true, the routine should simply ignore the fade parameters.
     *   
     *   Note that the fade setting specified here is independent of the
     *   end-of-track fade specified in play_sound().  The fade specified in
     *   play_sound() only applies when the track plays all the way through
     *   to the end.  The cancel_sound() fade, in contrast, specifies what
     *   should happen immediately at the time of the cancellation, so it
     *   effectively supersedes any play_sound() fade.  In particular, if
     *   'fade_out_ms' in cancel_sound() is zero, the sound should be stopped
     *   abruptly with no fade, EVEN IF play_sound() specified a fade-out.
     *   
     *   HANDLING PLATFORM LIMITATIONS:
     *   
     *   1. If the local platform can't support concurrent playback of two or
     *   more sounds, this routine should simply ignore fade_in_bg and always
     *   do "foreground" fades - that is, still process the fade in the
     *   background with respect to the UI, but don't invoke the callback
     *   until after the fade is finished.  Waiting to invoke the callback
     *   will ensure that the queue manager doesn't attempt to start playing
     *   another sound until the current sound is terminated.  This approach
     *   will still create a relatively smooth transition effect between
     *   adjacent tracks by doing a fade-out and then a fade-in - not exactly
     *   what the caller asks for when requesting a cross-fade, but a
     *   reasonable facsimile.
     *   
     *   2. If it's not possible to perform the fade in the background with
     *   respect to the UI, the recommended fallback is to limit the fade to
     *   a relatively short interval (e.g., one second: so if the caller
     *   requests a 5000ms fade, limit it to 1000ms) and then perform the
     *   fade as a blocking operation.  Keeping the fade time short will
     *   minimize the UI impact of freezing the UI during the fade, while
     *   still smoothing out sound transitions to some degree.
     *   
     *   3. If it's not possible to perform fades at all, simply ignore the
     *   fade parameters and stop the sound abruptly.  
     */
    virtual void cancel_sound(class CHtmlSysWin *win, int sync,
                              long fade_out_ms, int fade_in_bg) = 0;

    /*
     *   Suspend playback if necessary to make the sound device available
     *   for another sound.  If the given new sound can't be played
     *   simultaneously with this sound, suspend this sound so that the
     *   new sound can play.  (This routine is not meant to actually play
     *   the new sound; it simply suspends playback of the current sound
     *   so that the caller can then play the new sound.)  Return true if
     *   we did indeed suspend the sound, false if not.
     *   
     *   On systems where multiple sounds of any type can be played
     *   simultaneously, this routine should simply return false.  If the
     *   system allows multiple sounds to be played as long as they're not
     *   of the same type, this routine should check the type of the new
     *   sound; if it's the same as the current sound, the routine should
     *   suspend the current sound and return true, otherwise return false.
     *   
     *   The callback indicating that the sound is finished should NOT be
     *   invoked by this routine, since the routine should merely pause
     *   the current sound, not cancel it entirely.  
     */
    virtual int maybe_suspend(class CHtmlSysSound *new_sound) = 0;

    /*
     *   Resume playback.  If playback was previously suspended with
     *   maybe_suspend(), this should continue playing where we left off. 
     */
    virtual void resume() = 0;
};

/*
 *   System MIDI sound object.
 */
class CHtmlSysSoundMidi: public CHtmlSysSound
{
public:
    virtual HTML_res_type_t get_res_type() const
        { return HTML_res_type_MIDI; }

    /*
     *   Create a new system-specific MIDI object.  This is a static
     *   routine that must be implemented for this class in a
     *   system-specific module.  This routine should return a new
     *   instance of the correct subclass of this class for the current
     *   operating system. 
     *   
     *   Note that 'filesize' is the size of the data starting at
     *   'seekpos' that the resource loader considers to be part of this
     *   resource; the actual file may be larger than this value, since
     *   other data may follow the resource in the file.
     *   
     *   'url' is provided for diagnostic purposes; it should be used in
     *   any error message that this routine generates (with
     *   oshtml_dbg_printf).  It can otherwise be ignored.
     *   
     *   The 'win' argument is provided so that the loader can check the
     *   current environment for any information necessary for loading the
     *   resource.
     */
    static CHtmlSysResource *create_midi(const class CHtmlUrl *url,
                                         const textchar_t *filename,
                                         unsigned long seekpos,
                                         unsigned long filesize,
                                         class CHtmlSysWin *win);
};

/*
 *   System WAV sound object.
 */
class CHtmlSysSoundWav: public CHtmlSysSound
{
public:
    virtual HTML_res_type_t get_res_type() const
        { return HTML_res_type_WAV; }

    /*
     *   Create a new system-specific WAV object.  This is a static
     *   routine that must be implemented for this class in a
     *   system-specific module.  This routine should return a new
     *   instance of the correct subclass of this class for the current
     *   operating system. 
     *   
     *   Note that 'filesize' is the size of the data starting at
     *   'seekpos' that the resource loader considers to be part of this
     *   resource; the actual file may be larger than this value, since
     *   other data may follow the resource in the file.
     *   
     *   'url' is provided for diagnostic purposes; it should be used in
     *   any error message that this routine generates (with
     *   oshtml_dbg_printf).  It can otherwise be ignored.
     *   
     *   The 'win' argument is provided so that the loader can check the
     *   current environment for any information necessary for loading the
     *   resource.  
     */
    static CHtmlSysResource *create_wav(const class CHtmlUrl *url,
                                        const textchar_t *filename,
                                        unsigned long seekpos,
                                        unsigned long filesize,
                                        class CHtmlSysWin *win);
};

/*
 *   System MPEG 2.0 audio layer 1/2/3 sound object.  This single object
 *   is used for all three MPEG 2.0 audio layers, since all subtypes use a
 *   common header format, which can be used to determine the appropriate
 *   decoding algorithm.  
 */
class CHtmlSysSoundMpeg: public CHtmlSysSound
{
public:
    virtual HTML_res_type_t get_res_type() const
        { return HTML_res_type_WAV; }

    /*
     *   Create a new system-specific MPEG 2.0 layer 1/2/3 sound object.
     *   This is a static routine that must be implemented for this class
     *   in a system-specific module.  This routine should return a new
     *   instance of the correct subclass of this class for the current
     *   operating system. 
     *   
     *   Note that 'filesize' is the size of the data starting at
     *   'seekpos' that the resource loader considers to be part of this
     *   resource; the actual file may be larger than this value, since
     *   other data may follow the resource in the file.
     *   
     *   'url' is provided for diagnostic purposes; it should be used in
     *   any error message that this routine generates (with
     *   oshtml_dbg_printf).  It can otherwise be ignored.
     *   
     *   The 'win' argument is provided so that the loader can check the
     *   current environment for any information necessary for loading the
     *   resource.  
     */
    static CHtmlSysResource *create_mpeg(const class CHtmlUrl *url,
                                         const textchar_t *filename,
                                         unsigned long seekpos,
                                         unsigned long filesize,
                                         class CHtmlSysWin *win);
};

/*
 *   System Ogg Vorbis sound object.  
 */
class CHtmlSysSoundOgg: public CHtmlSysSound
{
public:
    virtual HTML_res_type_t get_res_type() const
        { return HTML_res_type_OGG; }

    /*
     *   Create a new system-specific Ogg Vorbis object.  This is a static
     *   routine that must be implemented for this class in a system-specific
     *   module.  This routine should return a new instance of the correct
     *   subclass of this class for the current operating system. 
     *   
     *   Note that 'filesize' is the size of the data starting at 'seekpos'
     *   that the resource loader considers to be part of this resource; the
     *   actual file may be larger than this value, since other data may
     *   follow the resource in the file.
     *   
     *   'url' is provided for diagnostic purposes; it should be used in any
     *   error message that this routine generates (with oshtml_dbg_printf).
     *   It can otherwise be ignored.
     *   
     *   The 'win' argument is provided so that the loader can check the
     *   current environment for any information necessary for loading the
     *   resource.  
     */
    static CHtmlSysResource *create_ogg(const class CHtmlUrl *url,
                                        const textchar_t *filename,
                                        unsigned long seekpos,
                                        unsigned long filesize,
                                        class CHtmlSysWin *win);
};

#endif /* HTMLSYS_H */

