home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / dev / rkrm.lha / RKRM / Narrator / Full_Narrator.c next >
Encoding:
C/C++ Source or Header  |  1992-09-03  |  18.9 KB  |  614 lines

  1. /*
  2.  * Copyright (c) 1992 Commodore-Amiga, Inc.
  3.  * 
  4.  * This example is provided in electronic form by Commodore-Amiga, Inc. for 
  5.  * use with the "Amiga ROM Kernel Reference Manual: Devices", 3rd Edition, 
  6.  * published by Addison-Wesley (ISBN 0-201-56775-X).
  7.  * 
  8.  * The "Amiga ROM Kernel Reference Manual: Devices" contains additional 
  9.  * information on the correct usage of the techniques and operating system 
  10.  * functions presented in these examples.  The source and executable code 
  11.  * of these examples may only be distributed in free electronic form, via 
  12.  * bulletin board or as part of a fully non-commercial and freely 
  13.  * redistributable diskette.  Both the source and executable code (including 
  14.  * comments) must be included, without modification, in any copy.  This 
  15.  * example may not be published in printed form or distributed with any
  16.  * commercial product.  However, the programming techniques and support
  17.  * routines set forth in these examples may be used in the development
  18.  * of original executable software products for Commodore Amiga computers.
  19.  * 
  20.  * All other rights reserved.
  21.  * 
  22.  * This example is provided "as-is" and is subject to change; no
  23.  * warranties are made.  All use is at your own risk. No liability or
  24.  * responsibility is assumed.
  25.  *
  26.  ***************************************************************************
  27.  *
  28.  * Full_Narrator.c
  29.  *
  30.  * This example program sends a string of phonetic text to the narrator
  31.  * device and, while it is speaking, highlights, word-by-word, a
  32.  * corresponding English string.  In addition, mouth movements are drawn
  33.  * in a separate window.
  34.  *
  35.  * Compile with SAS C 5.10  lc -b1 -cfistq -v -y -L
  36.  *
  37.  * Requires Kickstart V37 or greater.
  38.  */
  39.  
  40. #include <exec/types.h>
  41. #include <exec/memory.h>
  42. #include <exec/libraries.h>
  43. #include <dos/dos.h>
  44. #include <intuition/intuition.h>
  45. #include <ctype.h>
  46. #include <exec/exec.h>
  47. #include <fcntl.h>
  48. #include <devices/narrator.h>
  49.  
  50. #include <clib/exec_protos.h>
  51. #include <clib/alib_protos.h>
  52. #include <clib/intuition_protos.h>
  53. #include <clib/graphics_protos.h>
  54. #include <clib/dos_protos.h>
  55.  
  56. #include <stdlib.h>
  57. #include <string.h>
  58. #include <stdio.h>
  59.  
  60. #ifdef LATTICE
  61. int CXBRK(void) { return(0); }     /* Disable SAS CTRL/C handling */
  62. int chkabort(void) { return(0); }  /* really */
  63. #endif
  64.  
  65.  
  66. /*
  67.  *  Due to an omission, the sync field defines were not included in older
  68.  *  versions of the narrator device include files.  So, if they haven't
  69.  *  already been defined, do so now.
  70.  */
  71.  
  72. #ifndef NDF_READMOUTH                        /* Already defined ? */
  73. #define NDF_READMOUTH   0x01                 /* No, define here   */
  74. #define NDF_READWORD    0x02
  75. #define NDF_READSYL     0x04
  76. #endif
  77.  
  78. #define PEN3    3                            /* Drawing pens */
  79. #define PEN2    2
  80. #define PEN1    1
  81. #define PEN0    0
  82.  
  83.  
  84. BOOL FromCLI = TRUE;
  85. BYTE chans[4] = {3, 5, 10, 12};
  86.  
  87.  
  88. LONG EyesLeft;                               /* Left edge of left eye     */
  89. LONG EyesTop;                                /* Top of eyes box           */
  90. LONG EyesBottom;                             /* Bottom of eyes box        */
  91. LONG YMouthCenter;                           /* Pixels from top edge      */
  92. LONG XMouthCenter;                           /* Pixels from left edge     */
  93. LONG LipWidth, LipHeight;                    /* Width and height of mouth */
  94.  
  95.  
  96. struct TextAttr MyFont = {"topaz.font", TOPAZ_SIXTY, FS_NORMAL, FPF_ROMFONT,};
  97.  
  98. extern struct Library   *SysBase;   /* Used to check the OS version number */
  99.  
  100. struct  IntuitionBase   *IntuitionBase = NULL;
  101. struct  GfxBase         *GfxBase = NULL;
  102.  
  103. struct  MsgPort         *VoicePort = NULL;
  104. struct  MsgPort         *MouthPort = NULL;
  105.  
  106. struct  narrator_rb     *VoiceIO = NULL;
  107. struct  mouth_rb        *MouthIO = NULL;
  108.  
  109. struct  IntuiText  HighLight;
  110. struct  NewWindow  NewWindow;
  111. struct  Window    *TextWindow;
  112. struct  Window    *FaceWindow;
  113. struct  RastPort  *FaceRast;
  114.  
  115.  
  116. void main(int argc, char **argv)
  117. {
  118. LONG     i;
  119. LONG     sentence;
  120. LONG     Offset;
  121. LONG     CharsLeft;
  122. LONG     ScreenPos;
  123. LONG     WordLength;
  124. LONG     LineNum;
  125. UBYTE    *Tempptr;
  126. UBYTE    *English;
  127. UBYTE    *OldEnglish;
  128. UBYTE     c;
  129.  
  130. UBYTE    *PhonPtr;              /* Pointer to phonetic text     */
  131. LONG     PhonSize;              /* Size of phonetic text        */
  132. UBYTE    *PhonStart[100];       /* Start of phonetic sentences  */
  133. LONG     NumPhonStarts;         /* Number of phonetic sentences */
  134.  
  135. UBYTE    *EngPtr;               /* Pointer to English text      */
  136. LONG     EngSize;               /* Size of English text         */
  137. UBYTE    *EngStart[100];        /* Start of English sentences   */
  138. LONG     NumEngStarts;          /* Number of English sentences  */
  139.  
  140. UBYTE    *EngLine[24];          /* Start of line on screen      */
  141. LONG     EngBytes[24];          /* Bytes per line on screen     */
  142. LONG     NumEngLines;           /* Number of lines on screen    */
  143.  
  144.  
  145. extern  void  Cleanup(UBYTE *errmsg);
  146. extern  void  ClearWindow(struct Window *TextWindow);
  147. extern  void  DrawFace(void);
  148. extern  void  UpdateFace(void);
  149.  
  150.  
  151. /*
  152.  *  (0)   Note whether the program was started from the CLI or from
  153.  *        Workbench.
  154.  */
  155.  
  156. if (argc == 0)
  157.     FromCLI = FALSE;
  158.  
  159. /*
  160.  *  (1)   Setup the phonetic text to be spoken.  If there are any non-
  161.  *        alphabetic characters in the text (such as NEWLINES or TABS)
  162.  *        replace them with spaces.  Then break up the text into sentences,
  163.  *        storing the start of each sentence in PhonStart array elements.
  164.  */
  165.  
  166. PhonPtr = "KAA1RDIYOWMAYAA5PAXTHIY.  AY /HAED NEH1VER /HER4D AXV IHT "
  167.           "BIXFOH5R, BAHT DHEH5R IHT WAHZ - LIH4STIXD AEZ (DHAX FOH5RM "
  168.           "AXV /HAA5RT DIHZIY5Z) DHAET FEH4LD (NAAT WAH5N OHR TUW5) - "
  169.           "BAHT (AO7L THRIY5 AXV DHAX AA5RTAXFIHSHUL /HAA5RTQ "
  170.           "RIXSIH5PIYINTS).  (AH LIH5TUL RIXSER5CH) PROHDUW5ST (SAHM "
  171.           "IH5NTRIHSTIHNX RIXZAH5LTS). AHKOH5RDIHNX TUW (AEN AA5RTIHKUL "
  172.           "IHN DHAX NOWVEH5MBER EY2THQX NAY5NTIYNEYTIYFOH1R NUW IY5NXGLIND "
  173.           "JER5NUL AXV MEH5DIXSIN), (SIH5GEREHT SMOW5KIHNX) KAO4ZIHZ "
  174.           "(DHIHS LIY5THUL DIHZIY5Z) DHAET WIY4KINZ (DHAX /HAA5RTS "
  175.           "PAH4MPIHNX PAW2ER).  WAYL (DHIY IHGZAE5KT MEH5KINIXZUM) IHZ "
  176.           "NAAT KLIY5R, DAA5KTER AA5RTHER JEY2 /HAARTS SPEH5KYULEYTIHD "
  177.           "DHAET NIH4KAXTIY2N- OHR KAA5RBIN MUNAA5KSAYD IHN DHAX SMOW5K- "
  178.           "SAH5M/HAW1 POY4ZINZ DHAX /HAA5RT, AEND LIY4DZ TUW (/HAA5RT "
  179.           "FEY5LYER).";
  180.  
  181. PhonSize = strlen(PhonPtr);
  182. NumPhonStarts = 0;
  183. PhonStart[NumPhonStarts++] = PhonPtr;
  184. for (i = 0;  i < PhonSize;  ++i)
  185.      {
  186.      if (isspace((int)(c = *PhonPtr++)))
  187.          *(PhonPtr-1) = ' ';
  188.      if ((c == '.') || (c == '?'))
  189.          {
  190.          *PhonPtr = '\0';
  191.          PhonStart[NumPhonStarts++] = ++PhonPtr;
  192.          }
  193.      }
  194.  
  195. /*
  196.  *  (2)  Create the English text corresponding to the phonetic text above.
  197.  *       As before, insure that there are no TABS or NEWLINES in the text.
  198.  *       Break the text up into sentences and store the start of each
  199.  *       sentence in EngStart array elements.
  200.  */
  201.  
  202. EngPtr = "Cardiomyopathy. I had never heard of it before, but there it was "
  203.          "listed as the form of heart disease that felled not one or two but "
  204.          "all three of the artificial heart recipients. A little research "
  205.          "produced some interesting results. According to an article in the "
  206.          "November 8, 1984, New England Journal of Medicine, cigarette smoking "
  207.          "causes this lethal disease that weakens the heart's pumping power.   "
  208.          "While the exact mechanism is not clear, Doctor Arthur J Hartz "
  209.          "speculated that nicotine or carbon monoxide in the smoke somehow "
  210.          "poisons the heart and leads to heart failure.";
  211.  
  212. EngSize = strlen(EngPtr);
  213. NumEngStarts = 0;
  214. EngStart[NumEngStarts++] = EngPtr;
  215. for (i = 0;  i < EngSize;  ++i)
  216.      {
  217.      if (isspace((int)(c = *EngPtr++)))
  218.          *(EngPtr-1) = ' ';
  219.      if ((c == '.') || (c == '?'))
  220.          {
  221.          *EngPtr = '\0';
  222.          EngStart[NumEngStarts++] = ++EngPtr;
  223.          }
  224.      }
  225.  
  226. /*
  227.  *  (3)   Open Intuition and Graphics libraries.
  228.  */
  229.  
  230. if (!(IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0)))
  231.     Cleanup("can't open intuition");
  232.  
  233.  
  234. if ((GfxBase=(struct GfxBase *)OpenLibrary("graphics.library", 0)) == NULL)
  235.     Cleanup("can't open graphics");
  236.  
  237. /*
  238.  *  (4)   Setup the NewWindow structure for the text display and
  239.  *        open the text window.
  240.  */
  241.  
  242. NewWindow.LeftEdge    = 20;
  243. NewWindow.TopEdge     = 100;
  244. NewWindow.Width       = 600;
  245. NewWindow.Height      = 80;
  246. NewWindow.DetailPen   = 0;
  247. NewWindow.BlockPen    = 1;
  248. NewWindow.Title       = " Narrator Demo ";
  249. NewWindow.Flags       = SMART_REFRESH | ACTIVATE | WINDOWDEPTH | WINDOWDRAG;
  250. NewWindow.IDCMPFlags  = NULL;
  251. NewWindow.Type        = WBENCHSCREEN;
  252. NewWindow.FirstGadget = NULL;
  253. NewWindow.CheckMark   = NULL;
  254. NewWindow.Screen      = NULL;
  255. NewWindow.BitMap      = NULL;
  256. NewWindow.MinWidth    = 600;
  257. NewWindow.MinHeight   = 80;
  258. NewWindow.MaxWidth    = 600;
  259. NewWindow.MaxHeight   = 80;
  260.  
  261. if ((TextWindow = (struct Window *)OpenWindow(&NewWindow)) == NULL)
  262.     Cleanup("Text window could not be opened");
  263.  
  264. /*
  265.  *  (4)   Setup the NewWindow structure for the face display, open the 
  266.  *        window, cache the RastPort pointer, and draw the initial face.
  267.  *        If this is 1.3 or an earlier version of the OS (i.e., before V36) 
  268.  *        then don't do the face since it uses new features of Release 2
  269.  */
  270. if ( SysBase->lib_Version >= 36 )
  271.     {
  272.     NewWindow.LeftEdge    = 20;
  273.     NewWindow.TopEdge     = 12;
  274.     NewWindow.Width       = 120;
  275.     NewWindow.Height      = 80;
  276.     NewWindow.DetailPen   = 0;
  277.     NewWindow.BlockPen    = 1;
  278.     NewWindow.Title       = " Face ";
  279.     NewWindow.Flags       = SMART_REFRESH | WINDOWDEPTH | WINDOWDRAG;
  280.     NewWindow.IDCMPFlags  = NULL;
  281.     NewWindow.Type        = WBENCHSCREEN;
  282.     NewWindow.FirstGadget = NULL;
  283.     NewWindow.CheckMark   = NULL;
  284.     NewWindow.Screen      = NULL;
  285.     NewWindow.BitMap      = NULL;
  286.     NewWindow.MinWidth    = 120;
  287.     NewWindow.MinHeight   = 80;
  288.     NewWindow.MaxWidth    = 120;
  289.     NewWindow.MaxHeight   = 80;
  290.  
  291.     if ((FaceWindow = (struct Window *)OpenWindow(&NewWindow)) == NULL)
  292.         Cleanup("Face window could not be opened");
  293.  
  294.     FaceRast = FaceWindow->RPort;
  295.  
  296.     DrawFace();
  297.     }
  298.  
  299. /*
  300.  *  (5)   Create read and write msg ports.
  301.  */
  302.  
  303. if ((MouthPort = CreatePort(NULL,0)) == NULL)
  304.     Cleanup("Can't get read port");
  305. if ((VoicePort = CreatePort(NULL,0)) == NULL)
  306.     Cleanup("Can't get write port");
  307.  
  308. /*
  309.  *  (6)   Create read and write I/O request blocks.
  310.  */
  311.  
  312. if (!(MouthIO = (struct mouth_rb *)
  313.                   CreateExtIO(MouthPort,sizeof(struct mouth_rb))))
  314.     Cleanup("Can't get read IORB");
  315.  
  316. if (!(VoiceIO = (struct narrator_rb *)
  317.                    CreateExtIO(VoicePort,sizeof(struct narrator_rb))))
  318.     Cleanup("Can't get write IORB");
  319.  
  320. /*
  321.  *  (7)   Set up the write I/O request block and open the device.
  322.  */
  323.  
  324. VoiceIO->ch_masks            = &chans[0];
  325. VoiceIO->nm_masks            = sizeof(chans);
  326. VoiceIO->message.io_Command = CMD_WRITE;
  327. VoiceIO->flags               = NDF_NEWIORB;
  328.  
  329. if (OpenDevice("narrator.device", 0, VoiceIO, 0) != NULL)
  330.     Cleanup("OpenDevice failed");
  331.  
  332. /*
  333.  *  (8)   Set up the read I/O request block.
  334.  */
  335.  
  336. MouthIO->voice.message.io_Device = VoiceIO->message.io_Device;
  337. MouthIO->voice.message.io_Unit   = VoiceIO->message.io_Unit;
  338. MouthIO->voice.message.io_Message.mn_ReplyPort = MouthPort;
  339. MouthIO->voice.message.io_Command = CMD_READ;
  340.  
  341. /*
  342.  *  (9)   Initialize highlighting IntuiText structure.
  343.  */
  344.  
  345. HighLight.FrontPen  = 1;
  346. HighLight.BackPen   = 0;
  347. HighLight.DrawMode  = JAM1;
  348. HighLight.ITextFont = &MyFont;
  349. HighLight.NextText  = NULL;
  350.  
  351. /*
  352.  *  (10)   For each sentence, put up the English text in BLACK.  As
  353.  *         Narrator says each word, highlight that word in BLUE.  Also
  354.  *         continuously draw mouth shapes as Narrator speaks.
  355.  */
  356.  
  357. for (sentence = 0; sentence < NumPhonStarts; ++sentence)
  358.      {
  359.  
  360.      /*
  361.       *  (11)  Begin by breaking the English sentence up into lines of
  362.       *        text in the window.  EngLine is an array containing a
  363.       *        pointer to the start of each English text line.
  364.       */
  365.  
  366.      English = EngStart[sentence] + strspn((UBYTE *)EngStart[sentence], " ");
  367.      NumEngLines = 0;
  368.      EngLine[NumEngLines++] = English;
  369.      CharsLeft = strlen(English);
  370.      while (CharsLeft > 51)
  371.            {
  372.            for (Offset = 51; *(English+Offset) != ' '; --Offset) ;
  373.            EngBytes[NumEngLines-1] = Offset;
  374.            English                += Offset + 1;
  375.            *(English-1)            = '\0';
  376.            EngLine[NumEngLines++]  = English;
  377.            CharsLeft              -= Offset + 1;
  378.            }
  379.      EngBytes[NumEngLines-1] = CharsLeft;
  380.  
  381.      /*
  382.       *  (12)   Clear the window and draw in the unhighlighted English text.
  383.       */
  384.  
  385.      ClearWindow(TextWindow);
  386.  
  387.      HighLight.FrontPen = 1;
  388.      HighLight.LeftEdge = 10;
  389.      HighLight.TopEdge  = 20;
  390.  
  391.      for (i = 0; i < NumEngLines; ++i)
  392.           {
  393.           HighLight.IText = EngLine[i];
  394.           PrintIText(TextWindow->RPort, &HighLight, 0, 0);
  395.           HighLight.TopEdge += 10;
  396.           }
  397.  
  398.      HighLight.TopEdge  = 20;
  399.      HighLight.FrontPen = 3;
  400.      HighLight.IText    = EngLine[0];
  401.  
  402.      /*
  403.       *  (13)   Set up the write request with the address and length of
  404.       *         the phonetic text to be spoken.  Also tell device to
  405.       *         generate mouth shape changes and word sync events.
  406.       */
  407.  
  408.      VoiceIO->message.io_Data   = PhonStart[sentence];
  409.      VoiceIO->message.io_Length = strlen(VoiceIO->message.io_Data);
  410.      VoiceIO->flags             = NDF_NEWIORB | NDF_WORDSYNC;
  411.      VoiceIO->mouths            = 1;
  412.  
  413.     /*
  414.      *  (14)   Send the write request to the device.  This is an
  415.      *         asynchronous write, the device will return immediately.
  416.      */
  417.  
  418.      SendIO(VoiceIO);
  419.  
  420.     /*
  421.      *  (15)   Initialize some variables.
  422.      */
  423.  
  424.      ScreenPos  = 0;
  425.      LineNum    = 0;
  426.      English    = EngLine[LineNum];
  427.      OldEnglish = English;
  428.      MouthIO->voice.message.io_Error = 0;
  429.  
  430.     /*
  431.      *  (16)   Issue synchronous read requests.  For each request we
  432.      *         check the sync field to see if the read returned a mouth
  433.      *         shape change, a start of word sync event, or both.  We
  434.      *         continue issuing read requests until we get a return code
  435.      *         of ND_NoWrite, which indicates that the write has finished.
  436.      *         Since this section of code relies on new features of 
  437.      *         Release 2, first check to make sure we have the right OS.
  438.      */
  439.  
  440.      if ( SysBase->lib_Version >= 36 )
  441.          {
  442.          for ( DoIO(MouthIO);
  443.                MouthIO->voice.message.io_Error != ND_NoWrite;
  444.                DoIO(MouthIO) )
  445.  
  446.               {
  447.     
  448.               /*
  449.                *  (17)   If bit 1 of the sync field is on, this is a start
  450.                *         of word sync event.  In that case we highlight the
  451.                *         next word.
  452.                */
  453.   
  454.               if ( MouthIO->sync & NDF_READWORD )
  455.                   {
  456.                   if ((Tempptr = strchr(English, ' ')) != NULL)
  457.                       {
  458.                       English = Tempptr + 1;
  459.                       *(English-1) = '\0';
  460.                       }
  461.  
  462.                   PrintIText(TextWindow->RPort, &HighLight, 0, 0);
  463.                   WordLength      = strlen(OldEnglish) + 1;
  464.                   HighLight.IText = English;
  465.                   OldEnglish      = English;
  466.                   ScreenPos      += WordLength;
  467.  
  468.                   if (ScreenPos >= EngBytes[LineNum])
  469.                       {
  470.                       HighLight.LeftEdge   = 10;
  471.                       HighLight.TopEdge   += 10;
  472.                       ScreenPos            = 0;
  473.                       English = OldEnglish = EngLine[++LineNum];
  474.                       HighLight.IText      = English;
  475.                       }
  476.                   else
  477.                       HighLight.LeftEdge += 10*WordLength;
  478.                   }
  479.  
  480.               /*
  481.                *  (18)   If bit 0 of the sync field is on, this is a mouth
  482.                *         shape change event.  In that case we update the face.
  483.                *         If this is 1.3 or an earlier version of the OS (i.e., 
  484.                *         before V36) then don't do the face since it uses new 
  485.                *         features of Release 2.
  486.                */
  487.  
  488.               if (MouthIO->sync & NDF_READMOUTH) 
  489.                   UpdateFace();
  490.  
  491.               }
  492.           }
  493.  
  494.      /*   
  495.       *  (19)   The write has finished (return code from last read equals
  496.       *         ND_NoWrite).  We must wait on the write I/O request to
  497.       *         remove it from the message port.
  498.       */
  499.  
  500.      WaitIO(VoiceIO);
  501.  
  502.      }
  503.  
  504.  
  505. /*
  506.  *  (20)   Program completed, cleanup and return.
  507.  */
  508.  
  509. Cleanup("Normal completion");
  510.  
  511. }
  512.  
  513.  
  514. void Cleanup(UBYTE *errmsg)
  515. {
  516.  
  517.  
  518. /*
  519.  *  (1)   Cleanup and go away.  This routine does not return but EXITs.
  520.  *        Everything it does is pretty self explanatory.
  521.  */
  522.  
  523. if (FromCLI)
  524.     printf("%s\n\r", errmsg);
  525. if (TextWindow)
  526.     CloseWindow(TextWindow);
  527. if (FaceWindow)
  528.     CloseWindow(FaceWindow);
  529. if (VoiceIO && VoiceIO->message.io_Device)
  530.     CloseDevice(VoiceIO);
  531. if (VoiceIO)
  532.     DeleteExtIO(VoiceIO);
  533. if (VoicePort)
  534.     DeletePort(VoicePort);
  535. if (MouthIO)
  536.     DeleteExtIO(MouthIO);
  537. if (MouthPort)
  538.     DeletePort(MouthPort);
  539. if (GfxBase)
  540.     CloseLibrary(GfxBase);
  541. if (IntuitionBase)
  542.     CloseLibrary(IntuitionBase);
  543.  
  544. exit(RETURN_OK);
  545. }
  546.  
  547.  
  548. void ClearWindow(struct Window *TextWindow)
  549. {
  550. LONG    OldPen;
  551.  
  552. /*
  553.  *  (1)   Clears a window.
  554.  */
  555.  
  556. OldPen = (LONG)TextWindow->RPort->FgPen;
  557. SetAPen(TextWindow->RPort, 0);
  558. SetDrMd(TextWindow->RPort, JAM1);
  559. RectFill(TextWindow->RPort, 3, 12, TextWindow->Width-3, TextWindow->Height-2);
  560. SetAPen(TextWindow->RPort, OldPen);
  561. }
  562.  
  563.  
  564. void DrawFace()
  565. {
  566.  
  567. /*
  568.  *  (1)   Draws the initial face.  The variables defined here are used in
  569.  *        UpdateFace() to redraw the mouth shape.
  570.  */
  571.  
  572. EyesLeft = 15;
  573. EyesTop = 20;
  574. EyesBottom = 35;
  575.  
  576. XMouthCenter = FaceWindow->Width >> 1;
  577. YMouthCenter = FaceWindow->Height - 25;
  578.  
  579. SetAPen(FaceWindow->RPort, PEN1);
  580. RectFill(FaceWindow->RPort, 3, 10, FaceWindow->Width-3, FaceWindow->Height-2);
  581.  
  582. SetAPen(FaceWindow->RPort, PEN0);
  583. RectFill(FaceWindow->RPort, EyesLeft, EyesTop, EyesLeft+25, EyesTop+15);
  584. RectFill(FaceWindow->RPort, EyesLeft+65, EyesTop, EyesLeft+90, EyesTop+15);
  585.  
  586. SetAPen(FaceWindow->RPort, PEN3);
  587. Move(FaceWindow->RPort, XMouthCenter-(FaceWindow->Width >> 3), YMouthCenter);
  588. Draw(FaceWindow->RPort, XMouthCenter+(FaceWindow->Width >> 3), YMouthCenter);
  589. }
  590.  
  591.  
  592. void UpdateFace()
  593. {
  594.  
  595. /*
  596.  *  (1)   Redraws mouth shape in response to a mouth shape change message
  597.  *        from the device.  Its all pretty self explanatory.
  598.  */
  599.  
  600. WaitBOVP(&FaceWindow->WScreen->ViewPort);
  601. SetAPen(FaceRast, PEN1);
  602. RectFill(FaceRast, 3, EyesBottom, FaceWindow->Width-3, FaceWindow->Height-2);
  603.  
  604. LipWidth  = MouthIO->width*3;
  605. LipHeight = MouthIO->height*2/3;
  606.  
  607. SetAPen(FaceRast, PEN3);
  608. Move(FaceRast, XMouthCenter - LipWidth, YMouthCenter);
  609. Draw(FaceRast, XMouthCenter           , YMouthCenter - LipHeight);
  610. Draw(FaceRast, XMouthCenter + LipWidth, YMouthCenter);
  611. Draw(FaceRast, XMouthCenter,            YMouthCenter + LipHeight);
  612. Draw(FaceRast, XMouthCenter - LipWidth, YMouthCenter);
  613. }
  614.