home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD v1.2 / amidev_cd_12.iso / reference / amiga_mail_vol2 / iv-77 / optimrefresh.c < prev   
C/C++ Source or Header  |  1996-01-30  |  21KB  |  601 lines

  1. ;/* Optimrefresh.c - Execute me to compile me with SAS/C 6.56
  2. sc NMINC STRMERGE STREQ MCCONS COMNEST UNSCHAR NOSTKCHK SAVEDS IGNORE=73 optimrefresh.c
  3. slink FROM LIB:c.o,optimrefresh.o TO optimrefresh LIBRARY LIB:sc.lib,LIB:amiga.lib
  4. quit ;*/
  5.  
  6. /* Copyright © 1992 Martin Taillefer.   All rights reserved.              */
  7. /* The information contained herein is subject to change without notice,  */
  8. /* and is provided "as is" without warranty of any kind, either expressed */
  9. /* or implied.  The entire risk as to the use of this information is      */
  10. /* assumed by the user.                                                   */
  11.  
  12. /* This program demonstrates optimal window refreshing using a scrolling text
  13.  * display as a sample.
  14.  */
  15.  
  16. #include <exec/types.h>
  17. #include <exec/libraries.h>
  18. #include <exec/memory.h>
  19. #include <utility/hooks.h>
  20. #include <utility/tagitem.h>
  21. #include <graphics/gfxmacros.h>
  22. #include <intuition/intuition.h>
  23. #include <intuition/screens.h>
  24. #include <intuition/gadgetclass.h>
  25. #include <dos.h>
  26.  
  27. #include <clib/exec_protos.h>
  28. #include <clib/intuition_protos.h>
  29. #include <clib/graphics_protos.h>
  30. #include <clib/layers_protos.h>
  31. #include <clib/alib_protos.h>
  32. #include <clib/dos_protos.h>
  33.  
  34. /*****************************************************************************/
  35.  
  36.  
  37. /* There is one Line structure for every line of text in our fictional
  38.  * document.
  39.  */
  40. struct Line
  41. {
  42.     struct MinNode ln_Link;     /* to link the lines together           */
  43.     STRPTR         ln_Text;     /* pointer to the text for this line    */
  44.     ULONG          ln_TextLen;  /* the length of the text for this line */
  45. };
  46.  
  47.  
  48. /*****************************************************************************/
  49.  
  50.  
  51. /* system libraries */
  52. struct Library  *IntuitionBase;
  53. struct Library  *GfxBase;
  54. struct Library  *LayersBase;
  55.  
  56. /* global display handles */
  57. struct Screen   *screen;
  58. struct Window   *window;
  59. struct Gadget   *scroller;
  60. struct Hook      refreshHook;
  61.  
  62. struct RastPort render;
  63. struct RastPort clear;
  64.  
  65. /* our document along with associated view information */
  66. struct MinList   document;
  67. ULONG            numLines;
  68. ULONG            topLine;
  69. ULONG            oldTopLine;
  70. ULONG            linesVisible;
  71. ULONG            columnsVisible;
  72. ULONG            fontHeight;
  73. ULONG            fontWidth;
  74. ULONG            viewHeight;
  75. ULONG            viewWidth;
  76. ULONG            usefulWidth;
  77. ULONG            usefulHeight;
  78.  
  79. /* a state flag indicating whether the main application is busy */
  80. BOOL             taskBusy;
  81. /*****************************************************************************/
  82.  
  83. VOID InitDocument(VOID);
  84. VOID FreeDocument(VOID);
  85. VOID EventLoop(VOID);
  86. VOID __asm BackFillHook(register __a2 struct RastPort    *rp,
  87.                         register __a1 struct BackFillMsg *bfm);
  88.  
  89. /*****************************************************************************/
  90.  
  91.  
  92. /* This is where it all begins.
  93.  */
  94. ULONG main(void)
  95. {
  96.     /* open the system libraries we need.
  97.      */
  98.     IntuitionBase = OpenLibrary("intuition.library",37);
  99.     GfxBase       = OpenLibrary("graphics.library",37);
  100.     LayersBase    = OpenLibrary("layers.library",37);
  101.  
  102.     if (IntuitionBase && GfxBase && LayersBase)
  103.     {
  104.         /* get a pointer to the default public screen */
  105.         if (screen = LockPubScreen(NULL))
  106.         {
  107.             /* allocate and initialize a scroller as a BOOPSI object */
  108.             if (scroller = NewObject(NULL,"propgclass",
  109.                   GA_RelRight,    -13,
  110.                   GA_Top,         1+screen->WBorTop+screen->Font->ta_YSize+1,
  111.                   GA_Width,       10,
  112.                   GA_RelHeight,   -12-(screen->WBorTop+screen->Font->ta_YSize+1),
  113.                   GA_RelVerify,   TRUE,
  114.                   GA_Immediate,   TRUE,
  115.                   GA_FollowMouse, TRUE,
  116.                   GA_RightBorder, TRUE,
  117.                   PGA_Borderless, TRUE,
  118.                   PGA_Freedom,    FREEVERT,
  119.                   PGA_Total,      1,
  120.                   PGA_Visible,    1,
  121.                   PGA_Top,        0,
  122.                   PGA_NewLook,    TRUE,
  123.                   TAG_DONE))
  124.             {
  125.                 /* initialize data used by the backfill hook */
  126.                 refreshHook.h_Entry = ( ULONG (*)() )BackFillHook;  /* point the */
  127.                 taskBusy            = TRUE;               /* hook to our routine. */
  128.  
  129.                 /* open the window */
  130.                 if (window = OpenWindowTags(NULL,
  131.                       WA_Left,          0,
  132.                       WA_Top,           0,
  133.                       WA_PubScreen,     screen,
  134.                       WA_AutoAdjust,    TRUE,
  135.                       WA_CloseGadget,   TRUE,
  136.                       WA_DepthGadget,   TRUE,
  137.                       WA_DragBar,       TRUE,
  138.                       WA_SizeGadget,    TRUE,
  139.                       WA_SizeBRight,    TRUE,
  140.                       WA_Title,         "Optimized Refresh Sample",
  141.                       WA_SimpleRefresh, TRUE,
  142.                       WA_Activate,      TRUE,
  143.                       WA_Gadgets,       scroller,
  144.                       WA_MinWidth,      32,
  145.                       WA_MinHeight,     10+12+(screen->Font->ta_YSize+1),
  146.                       WA_MaxWidth,      -1,
  147.                       WA_MaxHeight,     -1,
  148.                       WA_IDCMP,         IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE
  149.                                         | IDCMP_REFRESHWINDOW | IDCMP_GADGETUP
  150.                                         | IDCMP_GADGETDOWN | IDCMP_MOUSEMOVE
  151.                                         | IDCMP_VANILLAKEY,
  152.                       WA_BackFill,      &refreshHook,
  153.                       TAG_DONE))
  154.                 {
  155.                     /* initialize our document structure */
  156.                     InitDocument();
  157.  
  158.                     /* process user events in the window */
  159.                     EventLoop();
  160.  
  161.                     /* free our document structure */
  162.                     FreeDocument();
  163.  
  164.                     /* close up shop */
  165.                     CloseWindow(window);
  166.                 }
  167.                 /* free the scroller BOOPSI object */
  168.                 DisposeObject(scroller);
  169.             }
  170.             /* unlock the default public screem */
  171.             UnlockPubScreen(NULL,screen);
  172.         }
  173.     }
  174.  
  175.     /* close the libraries we opened */
  176.     CloseLibrary(LayersBase);
  177.     CloseLibrary(GfxBase);
  178.     CloseLibrary(IntuitionBase);
  179.  
  180.     /* tell the shell everything is all right */
  181.     return(0);
  182. }
  183.  
  184.  
  185. /*****************************************************************************/
  186.  
  187.  
  188. /* This function initializes our document. That means allocating 100
  189.  * Line structures and linking them together in an Exec list. The lines
  190.  * are filled with a pattern of text so we have something to display
  191.  * in our window
  192.  */
  193. VOID InitDocument(VOID)
  194. {
  195. struct Line *line;
  196. UWORD        i,j;
  197.  
  198.     NewList((struct List *)&document);
  199.     numLines = 0;
  200.     i        = 100;
  201.     while (i--)
  202.     {
  203.         if (line = AllocVec(sizeof(struct Line)+91,MEMF_CLEAR|MEMF_PUBLIC))
  204.         {
  205.             line->ln_Text    = (STRPTR)((ULONG)line + sizeof(struct Line));
  206.             line->ln_TextLen = 40;
  207.             AddTail((struct List *)&document,(struct Node *)line);
  208.             numLines++;
  209.  
  210.             j = 0;
  211.             while (j < 90)
  212.             {
  213.                 line->ln_Text[j] = (numLines % 96) + 32;
  214.                 j++;
  215.             }
  216.         }
  217.     }
  218. }
  219.  
  220.  
  221. /*****************************************************************************/
  222.  
  223.  
  224. /* This function frees all the memory allocated by InitDocument() */
  225. VOID FreeDocument(VOID)
  226. {
  227. struct Line *line;
  228.  
  229.     while (line = (struct Line *)RemHead((struct List *)&document))
  230.         FreeVec(line);
  231. }
  232.  
  233.  
  234. /*****************************************************************************/
  235. /* This is the message packet passed by layers.library to a backfill hook.
  236.  * It contains a pointer to the layer that has been damaged, a Rectangle
  237.  * structure that defines the bounds of the damage. No rendering can occur
  238.  * outside of these coordinates.
  239.  *
  240.  * The backfill hook is also passed a RastPort in which the rendering
  241.  * should be performed.
  242.  */
  243. struct BackFillMsg
  244. {
  245.     struct Layer     *bf_Layer;
  246.     struct Rectangle  bf_Bounds;
  247.     LONG              bf_OffsetX;
  248.     LONG              bf_OffsetY;
  249. };
  250.  
  251.  
  252. VOID __asm BackFillHook(register __a2 struct RastPort    *rp,
  253.                         register __a1 struct BackFillMsg *bfm)
  254. {
  255. struct RastPort crp;
  256.  
  257.     crp       = *rp;            /* copy the rastport                      */
  258.     crp.Layer = NULL;           /* eliminate bogus clipping from our copy */
  259.  
  260.     if (taskBusy)
  261.     {
  262.         SetWrMsk(&crp,0xff);    /* if the main task is busy, clear all planes */
  263.     }
  264.     else
  265.     {
  266.         SetWrMsk(&crp,0xfe);    /* otherwise, clear all planes except plane 0 */
  267.     }
  268.  
  269.     SetAPen(&crp,0);                     /* set the pen to color 0         */
  270.     SetDrMd(&crp,JAM2);                  /* set the rendering mode we need */
  271.     RectFill(&crp,bfm->bf_Bounds.MinX,   /* clear the whole area           */
  272.                   bfm->bf_Bounds.MinY,
  273.                   bfm->bf_Bounds.MaxX,
  274.                   bfm->bf_Bounds.MaxY);
  275. }
  276.  
  277.  
  278. /*****************************************************************************/
  279.  
  280.  
  281. /* Adjust the scroller object to reflect the current window size and
  282.  * scroll offset within our document
  283.  */
  284. VOID SetScroller(struct Window *window, struct Gadget *scroller,
  285.                  ULONG linesVisible, ULONG numLines, ULONG topLines)
  286. {
  287.     SetGadgetAttrs(scroller,window,NULL,PGA_Visible, linesVisible,
  288.                                         PGA_Total,   numLines,
  289.                                         PGA_Top,     topLine,
  290.                                         TAG_DONE);
  291. }
  292.  
  293.  
  294. /*****************************************************************************/
  295.  
  296.  
  297. /* Render a single line of text at a given position */
  298. VOID RenderLine(UWORD x, UWORD y, UWORD w, STRPTR text, ULONG len)
  299. {
  300.     Move(&render,x,y);                  /* move the cursor to the position */
  301.  
  302.     if (len > columnsVisible)           /* is line is longer than allowed? */
  303.         len = columnsVisible;           /* yes, so reduce its length       */
  304.  
  305.     Text(&render,text,len);             /* write to the window             */
  306.  
  307.     if (len < columnsVisible)
  308.         RectFill(&clear,render.cp_x,y-render.TxBaseline,
  309.                         x+w-1,y-render.TxBaseline+fontHeight-1);
  310.  
  311. }
  312. /*****************************************************************************/
  313.  
  314. /* This function performs most of the rendering work needed by our sample.
  315.  * It first locks the window's layer to insure it doesn't get sized during
  316.  * the rendering process. It then looks at the current window size and
  317.  * adjusts its rendering variables in consequence. If the damage parameter
  318.  * is set to TRUE, the routine then proceeds to explicitly erase any area
  319.  * of the display to which we will not be rendering in the rendering loop.
  320.  * This erases any left over characters that could be left if the user sizes
  321.  * the window smaller. Finally, the routine determines which lines of the
  322.  * display need to be updated and goes on to do it.
  323.  */
  324. VOID RefreshView(BOOL damage)
  325. {
  326. ULONG        i;
  327. struct Line *line;
  328. UWORD        x,y;
  329.  
  330.     /* lock the window's layer so its size will not change */
  331.     LockLayer(NULL,window->WLayer);
  332.  
  333.     /* determine various values based on the current size of the window */
  334.     viewWidth      = window->Width - window->BorderLeft - window->BorderRight;
  335.     fontWidth      = window->RPort->Font->tf_XSize;
  336.     columnsVisible = viewWidth / fontWidth;
  337.  
  338.     viewHeight     = window->Height - window->BorderTop - window->BorderBottom;
  339.     fontHeight     = window->RPort->Font->tf_YSize;
  340.     linesVisible   = viewHeight / fontHeight;
  341.  
  342.     usefulWidth = columnsVisible * fontWidth;
  343.  
  344.     if (linesVisible > numLines)
  345.     {
  346.         usefulHeight = numLines * fontHeight;
  347.         topLine      = 0;
  348.     }
  349.     else if (topLine + linesVisible > numLines)
  350.     {
  351.         topLine      = (numLines - linesVisible);
  352.         usefulHeight = (numLines - topLine) * fontHeight;
  353.     }
  354.     else
  355.     {
  356.         usefulHeight = linesVisible * fontHeight;
  357.     }
  358.  
  359.     /* if we were called because of damage, we must erase any left over
  360.      * garbage
  361.      */
  362.     if (damage)
  363.     {
  364.         /* erase anything left over on the right side of the window */
  365.         if ((window->BorderLeft + usefulWidth < window->Width - window->BorderRight)
  366.         &&   usefulHeight)
  367.         {
  368.             RectFill(&clear,window->BorderLeft + usefulWidth,
  369.                             window->BorderTop,
  370.                             window->Width - window->BorderRight - 1,
  371.                             window->BorderTop + usefulHeight - 1);
  372.         }
  373.  
  374.         /* erase anything left over on the bottom of the window */
  375.         if ((window->BorderLeft < window->Width - window->BorderRight)
  376.         &&  (window->BorderTop + usefulHeight < window->Height - window->BorderBottom))
  377.         {
  378.             RectFill(&clear,window->BorderLeft,
  379.                             window->BorderTop + usefulHeight,
  380.                             window->Width - window->BorderRight - 1,
  381.                             window->Height - window->BorderBottom - 1);
  382.         }
  383.     }
  384.  
  385.     /* if we have at least one line and one column to render... */
  386.     if (usefulHeight && usefulWidth)
  387.     {
  388.         /* get a pointer to the first line currently visible */
  389.         line = (struct Line *)document.mlh_Head;
  390.         i    = topLine;
  391.         while (line->ln_Link.mln_Succ && i--)
  392.             line = (struct Line *)line->ln_Link.mln_Succ;
  393.  
  394.         if (damage
  395.         || (topLine >= oldTopLine + linesVisible - 1)
  396.         || ((oldTopLine > linesVisible)
  397.         && (topLine <= oldTopLine - linesVisible + 1)))
  398.         {
  399.             /* the whole display must be redrawn */
  400.             x = window->BorderLeft;
  401.             y = window->BorderTop + window->RPort->Font->tf_Baseline;
  402.             i = linesVisible;
  403.         }
  404.         else if (topLine < oldTopLine)
  405.         {
  406.             /* we just need to scroll the text */
  407.             ScrollRaster(&render,0,-(LONG)((oldTopLine - topLine) * fontHeight),
  408.                          window->BorderLeft,
  409.                          window->BorderTop,
  410.                          window->BorderLeft+usefulWidth-1,
  411.                          window->BorderTop+usefulHeight-1);
  412.  
  413.             /* indicates what section needs to be redrawn */
  414.             x = window->BorderLeft;
  415.             y = window->BorderTop + window->RPort->Font->tf_Baseline;
  416.             i = oldTopLine - topLine;
  417.         }
  418.         else if (topLine > oldTopLine)
  419.         {
  420.             /* we just need to scroll the text */
  421.             ScrollRaster(&render,0,(topLine - oldTopLine) * fontHeight,
  422.                          window->BorderLeft,
  423.                          window->BorderTop,
  424.                          window->BorderLeft+usefulWidth-1,
  425.                          window->BorderTop+usefulHeight-1);
  426.  
  427.             /* indicates what section needs to be redrawn */
  428.             i = linesVisible - (topLine - oldTopLine);
  429.             while (line->ln_Link.mln_Succ && i--)
  430.                 line = (struct Line *)line->ln_Link.mln_Succ;
  431.  
  432.             x = window->BorderLeft;
  433.             y = window->BorderTop + window->RPort->Font->tf_Baseline
  434.                 + (fontHeight * (linesVisible - (topLine - oldTopLine)));
  435.             i = topLine - oldTopLine;
  436.         }
  437.         else
  438.         {
  439.             /* we don't need to render anything */
  440.             i = 0;
  441.         }
  442.  
  443.         /* render all the lines we need */
  444.         while (i-- && line->ln_Link.mln_Succ)
  445.         {
  446.             RenderLine(x,y,usefulWidth,line->ln_Text,line->ln_TextLen);
  447.             y    += fontHeight;
  448.             line  = (struct Line *)line->ln_Link.mln_Succ;
  449.         }
  450.     }
  451.  
  452.     /* unlock the layer so normal operations can resume */
  453.     UnlockLayer(window->WLayer);
  454.  
  455.     /* keep track of what the current top line is. That way, when we
  456.      * come back in this routine later, and "topLine" has changed, we
  457.      * can tell how many lines we need to scroll in order to sync up the
  458.      * display
  459.      */
  460.     oldTopLine = topLine;
  461. }
  462.  
  463.  
  464. /*****************************************************************************/
  465.  
  466. /* Whenever the application is busy, this function is called. It will
  467.  * change the behavior of the backfill hook in order to improve the
  468.  * appearance of the display until the application completes its lengthy
  469.  * task.
  470.  *
  471.  * You could also set a busy pointer in the document window in this routine
  472.  * to tell the user you are not listening to him for awhile.
  473.  */
  474. VOID BusyState(BOOL makeBusy)
  475. {
  476.     taskBusy = makeBusy;
  477.  
  478.     if (LAYERREFRESH & window->WLayer->Flags)
  479.     {
  480.         BeginRefresh(window);
  481.         RefreshView(TRUE);
  482.         EndRefresh(window,TRUE);
  483.     }
  484. }
  485.  
  486.  
  487. /*****************************************************************************/
  488.  
  489.  
  490. /* This routine is a typical event processor. It looks and acts on all events
  491.  * arriving at the window's port.
  492.  */
  493. VOID EventLoop(VOID)
  494. {
  495. struct IntuiMessage *intuiMsg;
  496. ULONG                class;
  497.  
  498.     topLine    = 0;
  499.     oldTopLine = 0;
  500.  
  501.     /* initialize rendering attributes we are going to use */
  502.     render = *window->RPort;
  503.     SetDrMd(&render,JAM2);
  504.     SetWrMsk(&render,1);         /* we only want to render in the first plane */
  505.     SetAPen(&render,1);
  506.  
  507.     /* initialize clearing attributes we are going to use */
  508.     clear = *window->RPort;
  509.     SetDrMd(&clear,JAM2);
  510.     SetWrMsk(&clear,1);          /* we only want to clear the first plane */
  511.     SetAPen(&clear,0);
  512.  
  513.     /* render the initial display */
  514.     RefreshView(TRUE);
  515.  
  516.     /* set the initial scroller position and size */
  517.     SetScroller(window,scroller,linesVisible,numLines,topLine);
  518.  
  519.     /* we aren't busy, so register that fact */
  520.     BusyState(FALSE);
  521.  
  522.     while (TRUE)
  523.     {
  524.         /* if the LAYERREFRESH flag is set in the window's layer, it
  525.          * means the layer has some damage we should repair.
  526.          */
  527.         if (LAYERREFRESH & window->WLayer->Flags)
  528.         {
  529.             /* enter optimized repair state */
  530.             BeginRefresh(window);
  531.  
  532.             /* redraw the whole display through the optimized repair
  533.              * region
  534.              */
  535.             RefreshView(TRUE);
  536.  
  537.             /* tell the system we are done repairing the window
  538.              */
  539.             EndRefresh(window,TRUE);
  540.         }
  541.  
  542.  
  543.         /* nothing left to do but wait for user input */
  544.         WaitPort(window->UserPort);
  545.         intuiMsg = (struct IntuiMessage *)GetMsg(window->UserPort);
  546.         class    = intuiMsg->Class;
  547.         ReplyMsg(intuiMsg);
  548.  
  549.         /* we got a message, so act on it */
  550.         switch (class)
  551.         {
  552.             /* user clicked on the close gadget, exit the program */
  553.             case IDCMP_CLOSEWINDOW  : return;
  554.  
  555.             /* user sized the window. We need to redraw the whole
  556.              * display in order to eliminate any garbage. Start by
  557.              * calling BeginRefresh() and EndRefresh() to eliminate
  558.              * the window's damage regions then completely redraw
  559.              * the window contents.
  560.              */
  561.             case IDCMP_NEWSIZE      : BeginRefresh(window);
  562.                                       EndRefresh(window,TRUE);
  563.                                       RefreshView(TRUE);
  564.                                       SetScroller(window,
  565.                                                   scroller,
  566.                                                   linesVisible,
  567.                                                   numLines,
  568.                                                   topLine);
  569.                                       break;
  570.  
  571.             /* Intuition is telling us damage occured to our layer.
  572.              * Don't bother doing anything, the check at the top of the
  573.              * loop will catch this fact and refresh the display
  574.              *
  575.              * Even though we don't do anything with these events, we
  576.              * still need them to be sent to us so we will wake up and
  577.              * look at the LAYERREFRESH bit.
  578.              */
  579.             case IDCMP_REFRESHWINDOW: break;
  580.  
  581.             /* user is playing with the scroller. Get the scroller's current
  582.              * top line and synchronize the display to match it
  583.              */
  584.             case IDCMP_GADGETUP     :
  585.             case IDCMP_GADGETDOWN   :
  586.             case IDCMP_MOUSEMOVE    : GetAttr(PGA_Top,scroller,&topLine);
  587.                                       RefreshView(FALSE);
  588.                                       break;
  589.  
  590.             /* whenever a key is hit, we fake becoming busy for 4
  591.              * seconds. During that time, try to size and depth arrange
  592.              * the window to see what happens to its contents
  593.              */
  594.             case IDCMP_VANILLAKEY   : BusyState(TRUE);
  595.                                       Delay(200);
  596.                                       BusyState(FALSE);
  597.                                       break;
  598.         }
  599.     }
  600. }
  601.