
/*
**
** $VER: DBufDTAnim.c 1.3 (9.3.97)
**
** Double-buffered animation playback using an animation.datatype
** subclass for loading
**
** (C) Copyright 1996/1997 by Roland 'Gizzy' Mainz
**
** Based on 3.1_Examples2:intuition/doublebuffer.c
**
*/

/*----------------------------------------------------------------------*/

/* amiga includes */
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <graphics/displayinfo.h>
#include <graphics/videocontrol.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <datatypes/datatypes.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/soundclass.h>
#include <datatypes/pictureclass.h>
#include <datatypes/animationclass.h>
#include <libraries/gadtools.h>
#include <workbench/workbench.h>

/* amiga prototypes */
#include <clib/macros.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/icon_protos.h>
#include <clib/intuition_protos.h>
#include <clib/datatypes_protos.h>
#include <clib/dtclass_protos.h>
#include <clib/alib_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/alib_protos.h>

/* amiga pragmas */
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/datatypes_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/dtclass_pragmas.h>
#include <pragmas/alib_pragmas.h>

/* ansi includes */
#include <string.h>
#include <limits.h>

/*****************************************************************************/

#define TEMPLATE "FROM=NAME/A,"                      \
                 "STARTFRAME/K/N,"                   \
                 "STOPFRAME/K/N,"                    \
                 "SKIPFRAMES/K/N,"                   \
                 "IMMEDIATE/S,"                      \
                 "REPEAT=LOOP/S,"                    \
                 "DEFMON/S,"                         \
                 "VOL=VOLUME/K/N,"                   \
                 "VERBOSE/S"

#define OPT_NAME             (0)
#define OPT_STARTFRAME       (1)
#define OPT_STOPFRAME        (2)
#define OPT_SKIPFRAMES       (3)
#define OPT_IMMEDIATE        (4)
#define OPT_REPEAT           (5)
#define OPT_DEFMON           (6)
#define OPT_VOLUME           (7)
#define OPT_VERBOSE          (8)
#define NUM_OPTS             (9)

/*****************************************************************************/

/* prototypes */
STRPTR          init_all( void );
void            error_exit( STRPTR );
BOOL            handleIntuiMessage( struct IntuiMessage * );
void            handleDBufMessage( struct Message * );
ULONG           handleBufferSwap( void );
void            CloseWindowSafely( struct Window * );
void            StripIntuiMessages( struct MsgPort *, struct Window * );
void            CopyBitMap( struct BitMap *, struct BitMap * );
void            mysprintf( STRPTR, STRPTR, ... );
Object         *NewDTAnimationObjectA( STRPTR, struct TagItem * );
ULONG           GetDBufModeID( ULONG, BOOL );

/*****************************************************************************/

/* Some constants to handle the rendering of the animated face */
ULONG bm_width  = 0UL,
      bm_height = 0UL,
      bm_depth  = 0UL;

ULONG sc_id = 0UL;

/*****************************************************************************/

/* User interface constants and variables */
#define MENU_RUN     (1)
#define MENU_STEP    (2)
#define MENU_QUIT    (3)

const
UWORD pens[] =
{
    (UWORD)(~0U) /* terminator */
};

const
struct NewMenu demomenu[] =
{
    { NM_TITLE, "Project",           NULL,  0U, 0L, NULL               },
    { NM_ITEM,  "Run",               "R",   0U, 0L, (APTR)MENU_RUN     },
    { NM_ITEM,  "Step",              "S",   0U, 0L, (APTR)MENU_STEP    },
    { NM_ITEM,  NM_BARLABEL,         NULL,  0U, 0L, NULL               },
    { NM_ITEM,  "Quit",              "Q",   0U, 0L, (APTR)MENU_QUIT    },
    { NM_END,   NULL,                NULL,  0U, 0L, NULL               },
};

struct Screen   *canvassc   = NULL;
struct Window   *canvaswin  = NULL;
struct Menu     *menu       = NULL;
void            *canvasvi   = NULL;

/*****************************************************************************/

#define OK_REDRAW    (1)    /* Buffer fully detached, ready for redraw */
#define OK_SWAPIN    (2)    /* Buffer redrawn, ready for swap-in */

extern struct Library  *SysBase;
extern struct Library  *DOSBase;
       struct Library  *IconBase         = NULL;
       struct Library  *GfxBase          = NULL;
       struct Library  *IntuitionBase    = NULL;
       struct Library  *DataTypesBase    = NULL;
       struct Library  *GadToolsBase     = NULL;

struct MsgPort *dbufport = NULL;
struct MsgPort *userport = NULL;

struct ScreenBuffer *scbuf[ 2 ] =
{
    NULL,
    NULL
};

struct RastPort rport[ 2 ];

BOOL  standard_bitmap;

ULONG status[ 2 ];

ULONG          buf_current,
               buf_nextdraw,
               buf_nextswap;
ULONG          count;

struct adtFrame *face   = NULL;

/*****************************************************************************/


long main( void )
{
    /* Argument parsing variables */
    LONG                    options[ NUM_OPTS ];
    struct RDArgs          *rdargs;
    LONG                    error = 0L;

    memset( options, 0, sizeof( options ) );

    IconBase         = OpenLibrary( "icon.library",      39UL );
    GfxBase          = OpenLibrary( "graphics.library",  39UL );
    IntuitionBase    = OpenLibrary( "intuition.library", 39UL );
    DataTypesBase    = OpenLibrary( "datatypes.library", 39UL );
    GadToolsBase     = OpenLibrary( "gadtools.library",  39UL );

    if( IconBase && GfxBase && IntuitionBase && DataTypesBase && GadToolsBase )
    {
      STRPTR errorstring;

      if( rdargs = ReadArgs( TEMPLATE, options, NULL ) )
      {
        LONG                startframe  = 0UL,      /* start at this frame           */
                            stopframe   = LONG_MAX, /* stop at this frame            */
                            skipframes  = 1UL;      /* skip n frames during scan     */

        Object             *o;                      /* anim/movie object             */
        
        if( options[ OPT_STARTFRAME ] )
        {
          startframe = *((LONG *)options[ OPT_STARTFRAME ]);
        }

        if( options[ OPT_STOPFRAME ] )
        {
          stopframe = *((LONG *)options[ OPT_STOPFRAME ]);

          if( stopframe < 0L )
          {
            stopframe = 0L;
          }
        }

        if( options[ OPT_SKIPFRAMES ] )
        {
          skipframes = *((LONG *)options[ OPT_SKIPFRAMES ]);
        }

        /* Open the animation/movie object */
        if( o = NewDTAnimationObjectA( (STRPTR)options[ OPT_NAME ], NULL ) )
        {
          struct gpLayout         gpl;

          /* Base information */
          STRPTR                  objname;

          /* Animation related */
          ULONG                   animwidth,              /* width of animation */
                                  animheight,             /* height of animation */
                                  animdepth;              /* depth of animation */
          ULONG                   anumframes = 0UL;       /* number of frames in animation */
          ULONG                   afps = 0UL;

          /* Anim frame loading */
#define NUMALF (5)
          struct adtFrame         alf[ NUMALF ] = { 0 };  /* method msg for ADTM_LOADFRAME
                                                           * Here we're using an array of methods for bitmap caching.
                                                           * Caching bitmaps will speed-up loading when object uses
                                                           * a "rollback"-machanism for DLTAs (like IFF ANIM deltas or
                                                           * mpeg video macroblocks).
                                                           */
          UWORD                   alfindex = 0U;
          LONG                    timestamp;              /* timestamp to load */

          /* Animation frame picture related */
          ULONG                   amodeid;                /* mode id of animation */
          struct BitMapHeader    *abmh;                   /* bitmapheader of animation */
          ULONG                   anumcolors;             /* number of colors in animation */
          ULONG                  *acregs;                 /* ADTA_CRegs */
          struct ColorRegister   *acm;                    /* ADTA_ColorRegisters struct ColorRegister array */

          /* Anim frame sample related */
          ULONG                   avolume;

          BYTE                   *asample;
          ULONG                   asamplelength = 0UL;

          ULONG                   aperiod;
          ULONG                   acycles;

          Object                 *so = NULL;

          /* Get information about the object */
          if( GetDTAttrs( o, DTA_ObjName,          (&objname),
                             ADTA_Width,           (&animwidth),
                             ADTA_Height,          (&animheight),
                             ADTA_Depth,           (&animdepth),
                             ADTA_Frames,          (&anumframes),
                             ADTA_FramesPerSecond, (&afps),
                             ADTA_ModeID,          (&amodeid),
                             PDTA_BitMapHeader,    (&abmh),
                             ADTA_NumColors,       (&anumcolors),
                             ADTA_CRegs,           (&acregs),
                             ADTA_ColorRegisters,  (&acm),
                             ADTA_Volume,          (&avolume),
                             ADTA_Cycles,          (&acycles),
                             ADTA_Period,          (&aperiod),
                             ADTA_Sample,          (&asample),
                             ADTA_SampleLength,    (&asamplelength),
                             TAG_DONE ) != 16UL )
          {
            Printf( "*** warning: not enough attributes to get\n" );
          }

          bm_width  = animwidth;
          bm_height = animheight;
          bm_depth  = animdepth;
          sc_id     = GetDBufModeID( amodeid, (BOOL)options[ OPT_DEFMON ] );

          if( asamplelength && aperiod )
          {
            /* Create sound object for sound playback and set it up manually (e.g. DTST_RAM) */
            if( so = NewDTObject( NULL,
                                  DTA_SourceType,    DTST_RAM,
                                  DTA_GroupID,       GID_SOUND,
                                  SDTA_SignalTask,   FindTask( NULL ),
                                  SDTA_SignalBit,    SIGBREAKF_CTRL_F,
                                  SDTA_Volume,       ((options[ OPT_VOLUME ])?(*((LONG *)options[ OPT_VOLUME ])):(avolume)),
                                  SDTA_Cycles,       acycles,
                                  TAG_DONE ) )
            {
            }
          }


          /* Correct stopframe option */
          if( stopframe >= anumframes )
          {
            stopframe = anumframes - 1L;
          }

          /* Let's get everything initialized */
          if( !(errorstring = init_all()) )
          {
            ULONG i;

            /* Store initial colors into viewport */
            for( i = 0UL ; i < anumcolors ; i++ )
            {
              SetRGB32( (&(canvassc -> ViewPort)), i, acregs[ ((i * 3UL) + 0UL) ],
                                                      acregs[ ((i * 3UL) + 1UL) ],
                                                      acregs[ ((i * 3UL) + 2UL) ] );
            }

            /* Display any information we obtained */
            if( options[ OPT_VERBOSE ] )
            {
              if( objname )
              {
                Printf( "Name: \"%s\"\n", objname );
              }

              /* Print info... */
              Printf( "width %lu height %lu depth %lu frames %lu\n"
                      "FramesPerSecond %lu modeid %lx colors %lu\n"
                      "sample %lx samplelength %lu cycles %lu period %lu\n",
                       animwidth, animheight, animdepth, anumframes,
                       afps, amodeid, anumcolors,
                       asample, asamplelength, acycles, aperiod );
            }

            /* "Layout" animation (for those anim classes which need this,
             * __NOT__ recommened)
             */
            gpl . MethodID    = DTM_PROCLAYOUT;
            gpl . gpl_GInfo   = NULL;
            gpl . gpl_Initial = 1L;

            DoMethodA( o, (Msg)(&gpl) );

            {
              ULONG sigs          = 0UL;
              BOOL  terminated    = FALSE;
              BOOL  nextframe     = TRUE;
              BOOL  samplerunning = FALSE;

              count = (options[ OPT_IMMEDIATE ])?(~0UL):(0UL); /* Indicate whether we should immediately begin playing */

              buf_current     = 0UL;
              buf_nextdraw    = 1UL;
              buf_nextswap    = 1UL;

              timestamp = startframe;

              while( !terminated )
              {
                /* On error/end break */
                if( error || (((timestamp >= anumframes) || (timestamp < startframe) || (timestamp > stopframe)) && (!options[ OPT_REPEAT ])) )
                {
                  break;
                }

                if( nextframe )
                {
                  struct adtFrame *curralf = &alf[ alfindex ];

                  if( curralf -> alf_BitMap )
                  {
                    curralf -> MethodID = ADTM_UNLOADFRAME;
                    DoMethodA( o, (Msg)curralf );
                  }

                  /* reset method msg */
                  memset( (void *)curralf, 0, sizeof( struct adtFrame ) );

                  /* load frame */
                  curralf -> MethodID      = ADTM_LOADFRAME;
                  curralf -> alf_TimeStamp = timestamp;
                  curralf -> alf_Frame     = timestamp;
                  DoMethodA( o, (Msg)curralf );

                  if( options[ OPT_VERBOSE ] )
                  {
                    /* print frame contents */
                    Printf( "frame: timestamp %ld frame %lu duration %lu bitmap %lx cmap %lx sample %lx len %lu period %lu\n",
                            timestamp,
                            (curralf -> alf_Frame),
                            (curralf -> alf_Duration),
                            (curralf -> alf_BitMap),
                            (curralf -> alf_CMap),
                            (curralf -> alf_Sample),
                            (curralf -> alf_SampleLength),
                            (curralf -> alf_Period) );
                  }

                  /* Store current bitmap (which should be copied into the hidden display bitmap) */
                  face = curralf;

                  /* Next index in alf-cache */
                  alfindex = (alfindex + 1U) % NUMALF; /* Get next entry in the alf array */

                  /* Next timestamp to load */
                  timestamp += skipframes;

                  /* Repeat playing ? */
                  if( options[ OPT_REPEAT ] )
                  {
                    if( timestamp >= stopframe )
                    {
                      timestamp = startframe;
                    }

                    if( timestamp < startframe )
                    {
                      timestamp = stopframe;
                    }
                  }

                  nextframe = FALSE;
                }

                /* Check for and handle any IntuiMessages */
                if( sigs & (1UL << (userport -> mp_SigBit)) )
                {
                  struct IntuiMessage *imsg;

                  while( imsg = GT_GetIMsg( userport ) )
                  {
                    terminated |= handleIntuiMessage( imsg );
                    GT_ReplyIMsg( imsg );
                  }
                }

                /* Check for and handle any double-buffering messages.
                 * Note that double-buffering messages are "replied" to
                 * us, so we don't want to reply them to anyone.
                 */
                if( sigs & (1 << (dbufport -> mp_SigBit)) )
                {
                  struct Message *dbmsg;

                  while( dbmsg = GetMsg( dbufport ) )
                  {
                    handleDBufMessage( dbmsg );
                  }
                }

                /* Check for CTRL_C signal... */
                if( sigs & SIGBREAKF_CTRL_C )
                {
                  terminated = TRUE;
                  error = ERROR_BREAK;
                }

                if( so )
                {
                  if( sigs & SIGBREAKF_CTRL_F )
                  {
                    nextframe     = TRUE;
                    samplerunning = FALSE;
                  }
                }
                else
                {
                  nextframe = TRUE;
                }

                if( !terminated )
                {
                  ULONG held_off = 0UL;

                  /* Only handle swapping buffers if count is non-zero */
                  if( count )
                  {
                    held_off = handleBufferSwap();
                  }

                  if( held_off )
                  {
                    /* If were held-off at ChangeScreenBuffer() time, then we
                     * need to try ChangeScreenBuffer() again, without awaiting
                     * a signal.  We WaitTOF() to avoid busy-looping.
                     */
                    WaitTOF();
                  }
                  else
                  {
                    ULONG signals = (1UL << (dbufport -> mp_SigBit)) |
                                    (1UL << (userport -> mp_SigBit)) |
                                    SIGBREAKF_CTRL_F                 | /* wait for sound object */
                                    SIGBREAKF_CTRL_C;

                    /* Store sample data in the sound object */
                    if( so )
                    {
                      if( (face -> alf_Sample) && (samplerunning == FALSE) )
                      {
                        struct dtTrigger dtt;

                        SetDTAttrs( so, NULL, NULL,
                                    SDTA_Sample,       (face -> alf_Sample),
                                    SDTA_SampleLength, (face -> alf_SampleLength),
                                    SDTA_Period,       (face -> alf_Period),
                                    TAG_DONE );

                        /* Fill out the method message */
                        dtt . MethodID     = DTM_TRIGGER;
                        dtt . dtt_GInfo    = NULL;
                        dtt . dtt_Function = STM_PLAY;
                        dtt . dtt_Data     = NULL;

                        /* Play the sound */
                        DoDTMethodA( so, NULL, NULL, (Msg)(&dtt) );

                        samplerunning = TRUE;
                      }
                    }

                    if( nextframe && so )
                    {
                      sigs = SetSignal( 0UL, signals );
                    }
                    else
                    {
                      /* If we were not held-off, then we're all done
                       * with what we have to do.  We'll have no work to do
                       * until some kind of signal arrives.  This will normally
                       * be the arrival of the dbi_SafeMessage from the ROM
                       * double-buffering routines, but it might also be an
                       * IntuiMessage or a SIGBREAKF_CTRL_C signal from the user.
                       */
                      sigs = Wait( signals );
                    }
                  }
                }
              }
            }
            
            if( so )
            {
              Delay( TICKS_PER_SECOND );
            }

            /* Free all resources obtained by ADTM_LOADFRAME calls */
            for( i = 0UL ; i < NUMALF ; i++ )
            {
              if( alf[ i ] . alf_BitMap )
              {
                alf[ i ] . MethodID = ADTM_UNLOADFRAME;
                DoMethodA( o, (Msg)(&alf[ i ]) );
              }
            }
          }

          if( so )
          {
            /* The given data (e.g. SDTA_Sample) should't be freed by the object */
            SetDTAttrs( so, NULL, NULL, SDTA_Sample, NULL, TAG_DONE );

            DisposeDTObject( so );
          }

          error_exit( errorstring );

          /* Get rid of the anim/movie object */
          DisposeDTObject( o );
        }
        else
        {
          /* NewDTAnimationObjectA failed */
          error = IoErr();
        }

        /* Free the allocated memory after ReadArgs */
        FreeArgs( rdargs );
      }
      else
      {
        /* ReadArgs failed */
        error = IoErr();
      }
    }
    else
    {
      /* can't open required libraries */
      error = ERROR_OBJECT_NOT_FOUND;
    }

    /* Error ? */
    if( error )
    {
      TEXT errbuff[ 256 ];

      if( error >= DTERROR_UNKNOWN_DATATYPE )
      {
        mysprintf( errbuff, GetDTString( error ), "file" );
      }
      else
      {
        Fault( error, NULL, errbuff, sizeof( errbuff ) );
      }

      Printf( "DBufDTAnim: %s\n", errbuff );
    }

    CloseLibrary( GadToolsBase );
    CloseLibrary( DataTypesBase );
    CloseLibrary( IntuitionBase );
    CloseLibrary( GfxBase );
    CloseLibrary( IconBase );

    SetIoErr( error );

    return( ((error)?(RETURN_ERROR):(RETURN_OK)) );
}


/*****************************************************************************/

/* Handle the rendering and swapping of the buffers */
ULONG handleBufferSwap( void )
{
    ULONG held_off = 0UL;

    /* 'buf_nextdraw' is the next buffer to draw into.
     * The buffer is ready for drawing when we've received the
     * dbi_SafeMessage for that buffer.  Our routine to handle
     * messaging from the double-buffering functions sets the
     * OK_REDRAW flag when this message has appeared.
     *
     * Here, we set the OK_SWAPIN flag after we've redrawn
     * the imagery, since the buffer is ready to be swapped in.
     * We clear the OK_REDRAW flag, since we're done with redrawing
     */
    if( status[ buf_nextdraw ] == OK_REDRAW )
    {
      if( standard_bitmap )
      {
        CopyBitMap( (rport[ buf_nextdraw ] . BitMap), (face -> alf_BitMap) );
      }
      else
      {
        /* Assumes that BltBitMapRastPort is GFX-Card friendly;
         * and that GFX-Card-Software has patched BltBitMapRastPort to be able to use FAST-MEM
         */
        BltBitMapRastPort( (face -> alf_BitMap), 0L, 0L, (&rport[ buf_nextdraw ]), 0L, 0L, bm_width, bm_height, 0xc0 );
      }

      status[ buf_nextdraw ] = OK_SWAPIN;

      /* Toggle which the next buffer to draw is.
       * If you're using multiple ( >2 ) buffering, you
       * would use
       *
       *    buf_nextdraw = ( buf_nextdraw+1 ) % NUMBUFFERS;
       *
       */
      buf_nextdraw ^= 1;
    }

    /* Let's make sure that the next frame is rendered before we swap...
     */
    if( status[ buf_nextswap ] == OK_SWAPIN )
    {
      scbuf[ buf_nextswap ] -> sb_DBufInfo -> dbi_SafeMessage . mn_ReplyPort = dbufport;

      if( ChangeScreenBuffer( canvassc, scbuf[ buf_nextswap ] ) )
      {
        if( face -> alf_CMap )
        {
          ULONG *ctable;
          ULONG  numcolors = (1UL << (rport[ 0 ] . BitMap -> Depth));

          /* Alloc a table for copying the colormap */
          if( ctable = (ULONG *)AllocVec( ((numcolors + 5UL) * sizeof( ULONG ) * 3UL), (MEMF_PUBLIC | MEMF_CLEAR) ) )
          {
            ctable[ 0 ] = (numcolors << 16UL); /* set numcolors colors, beginning with colorindex 0 */

            GetRGB32( (face -> alf_CMap), 0UL, (numcolors - 1UL), (&ctable[ 1 ]) );

            LoadRGB32( (&(canvassc -> ViewPort)), ctable );

            FreeVec( ctable );
          }
          else
          {
            Printf( "no tmp color table mem\n" );
          }
        }

        status[ buf_nextswap ] = 0;

        buf_current = buf_nextswap;
        /* Toggle which the next buffer to swap in is.
         * If you're using multiple ( >2 ) buffering, you
         * would use
         *
         *    buf_nextswap = ( buf_nextswap+1 ) % NUMBUFFERS;
         *
         */
        buf_nextswap ^= 1;

        count--;
      }
      else
      {
        held_off = 1UL;
      }
    }

    return( held_off );
}

/*****************************************************************************/

/* Handle Intuition messages */
BOOL handleIntuiMessage( struct IntuiMessage *imsg )
{
    UWORD code       = imsg -> Code;
    BOOL  terminated = FALSE;

    switch( imsg -> Class )
    {
      case IDCMP_VANILLAKEY:
      {
          switch( code )
          {
            case 'S':
            case 's':
                count = 1UL;
                break;

            case 'R':
            case 'r':
                count = ~0UL;
                break;

            case 'Q':
            case 'q':
                count = 0UL;
                terminated = TRUE;
                break;
          }
      }
          break;

      case IDCMP_MENUPICK:
      {
          while( code != MENUNULL )
          {
            struct MenuItem *item;

            item = ItemAddress( menu, (ULONG)code );

            switch( (ULONG)GTMENUITEM_USERDATA( item ) )
            {
              case MENU_RUN:
              {
                  count = ~0UL;
              }
                  break;

              case MENU_STEP:
              {
                  count = 1UL;
              }
                  break;

              case MENU_QUIT:
              {
                  count = 0UL;
                  terminated = TRUE;
              }
                  break;
            }

            code = item -> NextSelect;
          }
      }
          break;
    }

    return( terminated );
}

/*****************************************************************************/

void handleDBufMessage( struct Message *dbmsg )
{
    ULONG buffer;

    /* dbi_SafeMessage is followed by an APTR dbi_UserData1, which
     * contains the buffer number.  This is an easy way to extract
     * it.
     * The dbi_SafeMessage tells us that it's OK to redraw the
     * in the previous buffer.
     */
    buffer = (ULONG)*((APTR **)(dbmsg + 1));

    /* Mark the previous buffer as OK to redraw into.
     * If you're using multiple ( >2 ) buffering, you
     * would use
     *
     *    ( buffer + NUMBUFFERS - 1 ) % NUMBUFFERS
     *
     */
    status[ (buffer ^ 1) ] = OK_REDRAW;
}

/*****************************************************************************/

/* Get the resources and objects we need */
STRPTR init_all( void )
{
    if( !( dbufport = CreateMsgPort() ) )
    {
      return( "Failed to create dbuf port\n" );
    }

    if( !( userport = CreateMsgPort() ) )
    {
      return( "Failed to create window port\n" );
    }

    if ( !(canvassc = OpenScreenTags( NULL, SA_DisplayID,      sc_id,
                                            SA_Overscan,       OSCAN_TEXT,
                                            SA_Width,          bm_width,
                                            SA_Height,         bm_height,
                                            SA_Depth,          bm_depth,
                                            SA_Interleaved,    FALSE,
                                            SA_AutoScroll,     TRUE,
                                            SA_Pens,           pens,
                                            SA_ShowTitle,      FALSE,
                                            SA_Title,          "DBufDTAnim",
                                            SA_SysFont,        1UL,
                                            TAG_DONE ) ) )
    {
      return( "Couldn't open screen\n" );
    }

    if( !(canvasvi = GetVisualInfo( canvassc, TAG_DONE )) )
    {
      return( "Couldn't get VisualInfo\n" );
    }

    if( !(canvaswin = OpenWindowTags( NULL, WA_NoCareRefresh, TRUE,
                                            WA_Activate,      TRUE,
                                            WA_Borderless,    TRUE,
                                            WA_Backdrop,      TRUE,
                                            WA_CustomScreen,  canvassc,
                                            WA_NewLookMenus,  TRUE,
                                            TAG_DONE ) ) )
    {
      return( "Couldn't open window\n" );
    }

    canvaswin -> UserPort = userport;

    ModifyIDCMP( canvaswin, (IDCMP_MENUPICK | IDCMP_VANILLAKEY) );

    if( !(menu = CreateMenus( (struct NewMenu *)demomenu, TAG_DONE )) )
    {
      return( "Couldn't create menus\n" );
    }

    if( !LayoutMenus( menu, canvasvi, GTMN_NewLookMenus, TRUE, TAG_DONE ) )
    {
      return( "Couldn't layout menus\n" );
    }

    SetMenuStrip( canvaswin, menu );

    if( !(scbuf[ 0 ] = AllocScreenBuffer( canvassc, NULL, SB_SCREEN_BITMAP )) )
    {
      return( "Couldn't allocate ScreenBuffer 1\n" );
    }

    if( !(scbuf[ 1 ] = AllocScreenBuffer( canvassc, NULL, SB_COPY_BITMAP )) )
    {
      return( "Couldn't allocate ScreenBuffer 2\n" );
    }

    /* Let's use the UserData to store the buffer number, for
     * easy identification when the message comes back.
     */
    scbuf[ 0 ] -> sb_DBufInfo -> dbi_UserData1 = (APTR)(0UL);
    scbuf[ 1 ] -> sb_DBufInfo -> dbi_UserData1 = (APTR)(1UL);
    status[ 0 ] = OK_REDRAW;
    status[ 1 ] = OK_REDRAW;

    InitRastPort( &rport[ 0 ] );
    InitRastPort( &rport[ 1 ] );
    rport[ 0 ] . BitMap = scbuf[ 0 ] -> sb_BitMap;
    rport[ 1 ] . BitMap = scbuf[ 1 ] -> sb_BitMap;

    /* Check if this is a standard Amiga-CHIPMEM-BitMap */
    standard_bitmap = (GetBitMapAttr( (scbuf[ 0 ] -> sb_BitMap), BMA_FLAGS ) & BMF_STANDARD);

    /* All is OK */
    return( 0 );
}

/*****************************************************************************/


/* Clean up everything and exit, printing the errorstring if any */
void error_exit( STRPTR errorstring )
{
    if( canvaswin )
    {
      ClearMenuStrip( canvaswin );
      CloseWindowSafely( canvaswin );
    }

    if( canvassc )
    {
      FreeScreenBuffer( canvassc, scbuf[ 1 ] );
      FreeScreenBuffer( canvassc, scbuf[ 0 ] );

      CloseScreen( canvassc );
    }

    if( dbufport )
    {
      DeleteMsgPort( dbufport );
    }

    if( userport )
    {
      DeleteMsgPort( userport );
    }

    FreeMenus( menu );
    FreeVisualInfo( canvasvi );

    if( errorstring )
    {
      Printf( errorstring );
    }
}


/* These functions close an Intuition window
 * that shares a port with other Intuition
 * windows or IPC customers.
 *
 * We are careful to set the UserPort to
 * null before closing, and to free
 * any messages that it might have been
 * sent.
 */

void CloseWindowSafely( struct Window *win )
{
    /* we forbid here to keep out of race conditions with Intuition */
    Forbid();

      /* send back any messages for this window
       * that have not yet been processed
       */
      StripIntuiMessages( (win -> UserPort), win );

      /* clear UserPort so Intuition will not free it */
      win -> UserPort = NULL;

      /* tell Intuition to stop sending more messages */
      ModifyIDCMP( win, 0UL );

    /* turn multitasking back on */
    Permit();

    /* and really close the window */
    CloseWindow( win );
}


/* Remove and reply all IntuiMessages on a port that have been sent to a particular window
 * (note that we don't rely on the ln_Succ pointer of a message after we have replied it )
 */
void StripIntuiMessages( struct MsgPort *mp, struct Window *win )
{
    struct IntuiMessage *imsg;
    struct Node         *succ;

    imsg = (struct IntuiMessage *)(mp -> mp_MsgList . lh_Head);

    while( succ = imsg -> ExecMessage . mn_Node . ln_Succ )
    {
      if( (imsg -> IDCMPWindow) == win )
      {
        /* Intuition is about to free this message.
         * Make sure that we have politely sent it back.
         */
        Remove( (&(imsg -> ExecMessage . mn_Node)) );

        ReplyMsg( (&(imsg -> ExecMessage)) );
      }

      imsg = (struct IntuiMessage *)succ;
    }
}


void CopyBitMap( struct BitMap *dest, struct BitMap *src )
{
    if( dest && src )
    {
      PLANEPTR  splanes[ 8 ] = { 0 };
      PLANEPTR  dplanes[ 8 ] = { 0 };
      ULONG     iRow,
                iPlane;

      for( iRow = (src -> Rows) ; iRow > 0UL ; iRow-- )
      {
        for( iPlane = 0UL ; iPlane < (ULONG)(src -> Depth) ; iPlane++ )
        {
          if( splanes[ iPlane ] == NULL ) splanes[ iPlane ] = src  -> Planes[ iPlane ];
          if( dplanes[ iPlane ] == NULL ) dplanes[ iPlane ] = dest -> Planes[ iPlane ];

          /* Write next row. */
          memcpy( dplanes[ iPlane ], splanes[ iPlane ], (src -> BytesPerRow) );

          splanes[ iPlane ] += src  -> BytesPerRow;
          dplanes[ iPlane ] += dest -> BytesPerRow;
        }
      }
    }
}


void mysprintf( STRPTR buffer, STRPTR fmt, ... )
{
    APTR args;

    args = (APTR)((&fmt) + 1);

    RawDoFmt( fmt, args, (void (*))"\x16\xc0\x4e\x75", buffer );
}


Object *NewDTAnimationObjectA( STRPTR name, struct TagItem *tags )
{
    Object *o     = NULL;
    LONG    error = 0L;

    if( name )
    {
      BPTR lock;

      if( lock = Lock( name, ACCESS_READ ) )
      {
        struct DataType *dtn;

        if( dtn = ObtainDataTypeA( DTST_FILE, (APTR)lock, NULL ) )
        {
          if( ((dtn -> dtn_Header -> dth_GroupID) == GID_MOVIE     ) ||
              ((dtn -> dtn_Header -> dth_GroupID) == GID_ANIMATION ) )
          {
            /* Open the animation/movie object;
             * ASSUMES that the file has not been changed during this scan...
             */
            if( !(o = NewDTObjectA( name, tags )) )
            {
              /* No object */
              error = IoErr();
            }
          }
          else
          {
            /* Not an animation or movie */
            error = ERROR_OBJECT_WRONG_TYPE;
          }

          ReleaseDataType( dtn );
        }
        else
        {
          /* Can't obtain data type */
          error = IoErr();
        }

        UnLock( lock );
      }
      else
      {
        /* Can't lock file */
        error = IoErr();
      }
    }

    SetIoErr( error );

    return( o );
}


ULONG GetDBufModeID( ULONG modeid, BOOL defmon )
{
    struct DisplayInfo disp = { 0 };

    if( defmon )
    {
      modeid &= ~MONITOR_ID_MASK; /* force the default monitor */
    }

    if( GetDisplayInfoData( NULL, (UBYTE *)(&disp), sizeof( struct DisplayInfo ), DTAG_DISP, modeid ) )
    {
      /* Does not support double-buffering ? */
      if( !(disp . PropertyFlags & DIPF_IS_DBUFFER) )
      {
        if( defmon )
        {
          Printf( "can't use default monitor\n" );
        }

        /* Get best modeid which supports double-buffering */
        modeid = BestModeID( BIDTAG_NominalWidth,  bm_width,
                             BIDTAG_NominalHeight, bm_height,
                             BIDTAG_NominalWidth,  bm_depth,
                             BIDTAG_SourceID,      modeid, /* Keep DIPF_#? properties of original modeid */
                             BIDTAG_DIPFMustHave,  DIPF_IS_DBUFFER,
                             TAG_DONE );
      }
    }
    else
    {
      Printf( "can't get displayinfo data\n" );
    }

    return( modeid );
}




