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
Wrap
C/C++ Source or Header
|
1996-01-30
|
21KB
|
601 lines
;/* Optimrefresh.c - Execute me to compile me with SAS/C 6.56
sc NMINC STRMERGE STREQ MCCONS COMNEST UNSCHAR NOSTKCHK SAVEDS IGNORE=73 optimrefresh.c
slink FROM LIB:c.o,optimrefresh.o TO optimrefresh LIBRARY LIB:sc.lib,LIB:amiga.lib
quit ;*/
/* Copyright © 1992 Martin Taillefer. All rights reserved. */
/* The information contained herein is subject to change without notice, */
/* and is provided "as is" without warranty of any kind, either expressed */
/* or implied. The entire risk as to the use of this information is */
/* assumed by the user. */
/* This program demonstrates optimal window refreshing using a scrolling text
* display as a sample.
*/
#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <utility/hooks.h>
#include <utility/tagitem.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <intuition/gadgetclass.h>
#include <dos.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/layers_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
/*****************************************************************************/
/* There is one Line structure for every line of text in our fictional
* document.
*/
struct Line
{
struct MinNode ln_Link; /* to link the lines together */
STRPTR ln_Text; /* pointer to the text for this line */
ULONG ln_TextLen; /* the length of the text for this line */
};
/*****************************************************************************/
/* system libraries */
struct Library *IntuitionBase;
struct Library *GfxBase;
struct Library *LayersBase;
/* global display handles */
struct Screen *screen;
struct Window *window;
struct Gadget *scroller;
struct Hook refreshHook;
struct RastPort render;
struct RastPort clear;
/* our document along with associated view information */
struct MinList document;
ULONG numLines;
ULONG topLine;
ULONG oldTopLine;
ULONG linesVisible;
ULONG columnsVisible;
ULONG fontHeight;
ULONG fontWidth;
ULONG viewHeight;
ULONG viewWidth;
ULONG usefulWidth;
ULONG usefulHeight;
/* a state flag indicating whether the main application is busy */
BOOL taskBusy;
/*****************************************************************************/
VOID InitDocument(VOID);
VOID FreeDocument(VOID);
VOID EventLoop(VOID);
VOID __asm BackFillHook(register __a2 struct RastPort *rp,
register __a1 struct BackFillMsg *bfm);
/*****************************************************************************/
/* This is where it all begins.
*/
ULONG main(void)
{
/* open the system libraries we need.
*/
IntuitionBase = OpenLibrary("intuition.library",37);
GfxBase = OpenLibrary("graphics.library",37);
LayersBase = OpenLibrary("layers.library",37);
if (IntuitionBase && GfxBase && LayersBase)
{
/* get a pointer to the default public screen */
if (screen = LockPubScreen(NULL))
{
/* allocate and initialize a scroller as a BOOPSI object */
if (scroller = NewObject(NULL,"propgclass",
GA_RelRight, -13,
GA_Top, 1+screen->WBorTop+screen->Font->ta_YSize+1,
GA_Width, 10,
GA_RelHeight, -12-(screen->WBorTop+screen->Font->ta_YSize+1),
GA_RelVerify, TRUE,
GA_Immediate, TRUE,
GA_FollowMouse, TRUE,
GA_RightBorder, TRUE,
PGA_Borderless, TRUE,
PGA_Freedom, FREEVERT,
PGA_Total, 1,
PGA_Visible, 1,
PGA_Top, 0,
PGA_NewLook, TRUE,
TAG_DONE))
{
/* initialize data used by the backfill hook */
refreshHook.h_Entry = ( ULONG (*)() )BackFillHook; /* point the */
taskBusy = TRUE; /* hook to our routine. */
/* open the window */
if (window = OpenWindowTags(NULL,
WA_Left, 0,
WA_Top, 0,
WA_PubScreen, screen,
WA_AutoAdjust, TRUE,
WA_CloseGadget, TRUE,
WA_DepthGadget, TRUE,
WA_DragBar, TRUE,
WA_SizeGadget, TRUE,
WA_SizeBRight, TRUE,
WA_Title, "Optimized Refresh Sample",
WA_SimpleRefresh, TRUE,
WA_Activate, TRUE,
WA_Gadgets, scroller,
WA_MinWidth, 32,
WA_MinHeight, 10+12+(screen->Font->ta_YSize+1),
WA_MaxWidth, -1,
WA_MaxHeight, -1,
WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE
| IDCMP_REFRESHWINDOW | IDCMP_GADGETUP
| IDCMP_GADGETDOWN | IDCMP_MOUSEMOVE
| IDCMP_VANILLAKEY,
WA_BackFill, &refreshHook,
TAG_DONE))
{
/* initialize our document structure */
InitDocument();
/* process user events in the window */
EventLoop();
/* free our document structure */
FreeDocument();
/* close up shop */
CloseWindow(window);
}
/* free the scroller BOOPSI object */
DisposeObject(scroller);
}
/* unlock the default public screem */
UnlockPubScreen(NULL,screen);
}
}
/* close the libraries we opened */
CloseLibrary(LayersBase);
CloseLibrary(GfxBase);
CloseLibrary(IntuitionBase);
/* tell the shell everything is all right */
return(0);
}
/*****************************************************************************/
/* This function initializes our document. That means allocating 100
* Line structures and linking them together in an Exec list. The lines
* are filled with a pattern of text so we have something to display
* in our window
*/
VOID InitDocument(VOID)
{
struct Line *line;
UWORD i,j;
NewList((struct List *)&document);
numLines = 0;
i = 100;
while (i--)
{
if (line = AllocVec(sizeof(struct Line)+91,MEMF_CLEAR|MEMF_PUBLIC))
{
line->ln_Text = (STRPTR)((ULONG)line + sizeof(struct Line));
line->ln_TextLen = 40;
AddTail((struct List *)&document,(struct Node *)line);
numLines++;
j = 0;
while (j < 90)
{
line->ln_Text[j] = (numLines % 96) + 32;
j++;
}
}
}
}
/*****************************************************************************/
/* This function frees all the memory allocated by InitDocument() */
VOID FreeDocument(VOID)
{
struct Line *line;
while (line = (struct Line *)RemHead((struct List *)&document))
FreeVec(line);
}
/*****************************************************************************/
/* This is the message packet passed by layers.library to a backfill hook.
* It contains a pointer to the layer that has been damaged, a Rectangle
* structure that defines the bounds of the damage. No rendering can occur
* outside of these coordinates.
*
* The backfill hook is also passed a RastPort in which the rendering
* should be performed.
*/
struct BackFillMsg
{
struct Layer *bf_Layer;
struct Rectangle bf_Bounds;
LONG bf_OffsetX;
LONG bf_OffsetY;
};
VOID __asm BackFillHook(register __a2 struct RastPort *rp,
register __a1 struct BackFillMsg *bfm)
{
struct RastPort crp;
crp = *rp; /* copy the rastport */
crp.Layer = NULL; /* eliminate bogus clipping from our copy */
if (taskBusy)
{
SetWrMsk(&crp,0xff); /* if the main task is busy, clear all planes */
}
else
{
SetWrMsk(&crp,0xfe); /* otherwise, clear all planes except plane 0 */
}
SetAPen(&crp,0); /* set the pen to color 0 */
SetDrMd(&crp,JAM2); /* set the rendering mode we need */
RectFill(&crp,bfm->bf_Bounds.MinX, /* clear the whole area */
bfm->bf_Bounds.MinY,
bfm->bf_Bounds.MaxX,
bfm->bf_Bounds.MaxY);
}
/*****************************************************************************/
/* Adjust the scroller object to reflect the current window size and
* scroll offset within our document
*/
VOID SetScroller(struct Window *window, struct Gadget *scroller,
ULONG linesVisible, ULONG numLines, ULONG topLines)
{
SetGadgetAttrs(scroller,window,NULL,PGA_Visible, linesVisible,
PGA_Total, numLines,
PGA_Top, topLine,
TAG_DONE);
}
/*****************************************************************************/
/* Render a single line of text at a given position */
VOID RenderLine(UWORD x, UWORD y, UWORD w, STRPTR text, ULONG len)
{
Move(&render,x,y); /* move the cursor to the position */
if (len > columnsVisible) /* is line is longer than allowed? */
len = columnsVisible; /* yes, so reduce its length */
Text(&render,text,len); /* write to the window */
if (len < columnsVisible)
RectFill(&clear,render.cp_x,y-render.TxBaseline,
x+w-1,y-render.TxBaseline+fontHeight-1);
}
/*****************************************************************************/
/* This function performs most of the rendering work needed by our sample.
* It first locks the window's layer to insure it doesn't get sized during
* the rendering process. It then looks at the current window size and
* adjusts its rendering variables in consequence. If the damage parameter
* is set to TRUE, the routine then proceeds to explicitly erase any area
* of the display to which we will not be rendering in the rendering loop.
* This erases any left over characters that could be left if the user sizes
* the window smaller. Finally, the routine determines which lines of the
* display need to be updated and goes on to do it.
*/
VOID RefreshView(BOOL damage)
{
ULONG i;
struct Line *line;
UWORD x,y;
/* lock the window's layer so its size will not change */
LockLayer(NULL,window->WLayer);
/* determine various values based on the current size of the window */
viewWidth = window->Width - window->BorderLeft - window->BorderRight;
fontWidth = window->RPort->Font->tf_XSize;
columnsVisible = viewWidth / fontWidth;
viewHeight = window->Height - window->BorderTop - window->BorderBottom;
fontHeight = window->RPort->Font->tf_YSize;
linesVisible = viewHeight / fontHeight;
usefulWidth = columnsVisible * fontWidth;
if (linesVisible > numLines)
{
usefulHeight = numLines * fontHeight;
topLine = 0;
}
else if (topLine + linesVisible > numLines)
{
topLine = (numLines - linesVisible);
usefulHeight = (numLines - topLine) * fontHeight;
}
else
{
usefulHeight = linesVisible * fontHeight;
}
/* if we were called because of damage, we must erase any left over
* garbage
*/
if (damage)
{
/* erase anything left over on the right side of the window */
if ((window->BorderLeft + usefulWidth < window->Width - window->BorderRight)
&& usefulHeight)
{
RectFill(&clear,window->BorderLeft + usefulWidth,
window->BorderTop,
window->Width - window->BorderRight - 1,
window->BorderTop + usefulHeight - 1);
}
/* erase anything left over on the bottom of the window */
if ((window->BorderLeft < window->Width - window->BorderRight)
&& (window->BorderTop + usefulHeight < window->Height - window->BorderBottom))
{
RectFill(&clear,window->BorderLeft,
window->BorderTop + usefulHeight,
window->Width - window->BorderRight - 1,
window->Height - window->BorderBottom - 1);
}
}
/* if we have at least one line and one column to render... */
if (usefulHeight && usefulWidth)
{
/* get a pointer to the first line currently visible */
line = (struct Line *)document.mlh_Head;
i = topLine;
while (line->ln_Link.mln_Succ && i--)
line = (struct Line *)line->ln_Link.mln_Succ;
if (damage
|| (topLine >= oldTopLine + linesVisible - 1)
|| ((oldTopLine > linesVisible)
&& (topLine <= oldTopLine - linesVisible + 1)))
{
/* the whole display must be redrawn */
x = window->BorderLeft;
y = window->BorderTop + window->RPort->Font->tf_Baseline;
i = linesVisible;
}
else if (topLine < oldTopLine)
{
/* we just need to scroll the text */
ScrollRaster(&render,0,-(LONG)((oldTopLine - topLine) * fontHeight),
window->BorderLeft,
window->BorderTop,
window->BorderLeft+usefulWidth-1,
window->BorderTop+usefulHeight-1);
/* indicates what section needs to be redrawn */
x = window->BorderLeft;
y = window->BorderTop + window->RPort->Font->tf_Baseline;
i = oldTopLine - topLine;
}
else if (topLine > oldTopLine)
{
/* we just need to scroll the text */
ScrollRaster(&render,0,(topLine - oldTopLine) * fontHeight,
window->BorderLeft,
window->BorderTop,
window->BorderLeft+usefulWidth-1,
window->BorderTop+usefulHeight-1);
/* indicates what section needs to be redrawn */
i = linesVisible - (topLine - oldTopLine);
while (line->ln_Link.mln_Succ && i--)
line = (struct Line *)line->ln_Link.mln_Succ;
x = window->BorderLeft;
y = window->BorderTop + window->RPort->Font->tf_Baseline
+ (fontHeight * (linesVisible - (topLine - oldTopLine)));
i = topLine - oldTopLine;
}
else
{
/* we don't need to render anything */
i = 0;
}
/* render all the lines we need */
while (i-- && line->ln_Link.mln_Succ)
{
RenderLine(x,y,usefulWidth,line->ln_Text,line->ln_TextLen);
y += fontHeight;
line = (struct Line *)line->ln_Link.mln_Succ;
}
}
/* unlock the layer so normal operations can resume */
UnlockLayer(window->WLayer);
/* keep track of what the current top line is. That way, when we
* come back in this routine later, and "topLine" has changed, we
* can tell how many lines we need to scroll in order to sync up the
* display
*/
oldTopLine = topLine;
}
/*****************************************************************************/
/* Whenever the application is busy, this function is called. It will
* change the behavior of the backfill hook in order to improve the
* appearance of the display until the application completes its lengthy
* task.
*
* You could also set a busy pointer in the document window in this routine
* to tell the user you are not listening to him for awhile.
*/
VOID BusyState(BOOL makeBusy)
{
taskBusy = makeBusy;
if (LAYERREFRESH & window->WLayer->Flags)
{
BeginRefresh(window);
RefreshView(TRUE);
EndRefresh(window,TRUE);
}
}
/*****************************************************************************/
/* This routine is a typical event processor. It looks and acts on all events
* arriving at the window's port.
*/
VOID EventLoop(VOID)
{
struct IntuiMessage *intuiMsg;
ULONG class;
topLine = 0;
oldTopLine = 0;
/* initialize rendering attributes we are going to use */
render = *window->RPort;
SetDrMd(&render,JAM2);
SetWrMsk(&render,1); /* we only want to render in the first plane */
SetAPen(&render,1);
/* initialize clearing attributes we are going to use */
clear = *window->RPort;
SetDrMd(&clear,JAM2);
SetWrMsk(&clear,1); /* we only want to clear the first plane */
SetAPen(&clear,0);
/* render the initial display */
RefreshView(TRUE);
/* set the initial scroller position and size */
SetScroller(window,scroller,linesVisible,numLines,topLine);
/* we aren't busy, so register that fact */
BusyState(FALSE);
while (TRUE)
{
/* if the LAYERREFRESH flag is set in the window's layer, it
* means the layer has some damage we should repair.
*/
if (LAYERREFRESH & window->WLayer->Flags)
{
/* enter optimized repair state */
BeginRefresh(window);
/* redraw the whole display through the optimized repair
* region
*/
RefreshView(TRUE);
/* tell the system we are done repairing the window
*/
EndRefresh(window,TRUE);
}
/* nothing left to do but wait for user input */
WaitPort(window->UserPort);
intuiMsg = (struct IntuiMessage *)GetMsg(window->UserPort);
class = intuiMsg->Class;
ReplyMsg(intuiMsg);
/* we got a message, so act on it */
switch (class)
{
/* user clicked on the close gadget, exit the program */
case IDCMP_CLOSEWINDOW : return;
/* user sized the window. We need to redraw the whole
* display in order to eliminate any garbage. Start by
* calling BeginRefresh() and EndRefresh() to eliminate
* the window's damage regions then completely redraw
* the window contents.
*/
case IDCMP_NEWSIZE : BeginRefresh(window);
EndRefresh(window,TRUE);
RefreshView(TRUE);
SetScroller(window,
scroller,
linesVisible,
numLines,
topLine);
break;
/* Intuition is telling us damage occured to our layer.
* Don't bother doing anything, the check at the top of the
* loop will catch this fact and refresh the display
*
* Even though we don't do anything with these events, we
* still need them to be sent to us so we will wake up and
* look at the LAYERREFRESH bit.
*/
case IDCMP_REFRESHWINDOW: break;
/* user is playing with the scroller. Get the scroller's current
* top line and synchronize the display to match it
*/
case IDCMP_GADGETUP :
case IDCMP_GADGETDOWN :
case IDCMP_MOUSEMOVE : GetAttr(PGA_Top,scroller,&topLine);
RefreshView(FALSE);
break;
/* whenever a key is hit, we fake becoming busy for 4
* seconds. During that time, try to size and depth arrange
* the window to see what happens to its contents
*/
case IDCMP_VANILLAKEY : BusyState(TRUE);
Delay(200);
BusyState(FALSE);
break;
}
}
}