home *** CD-ROM | disk | FTP | other *** search
- /*
-
- This little demo show how to write classes which need a long
- time to render their contents. In this case, we take a little
- fractal algorithm as example. The actual calculations are
- done in a separate task, the display is updated from time
- to time.
-
- */
-
- #include "demo.h"
- #include <dos/dostags.h>
- #include <exec/memory.h>
-
-
- /* Pixel dimensions of our fractal */
-
- #define FRACTALWIDTH 300
- #define FRACTALHEIGHT 300
-
-
- /* Fractal Description */
-
- struct FractalDesc
- {
- float left;
- float right;
- float top;
- float bottom;
- };
-
- #define MaxIterations 60
-
- /* Attributes and methods for the custom class */
-
- #define MUISERIALNR_STUNTZI 1
- #define TAGBASE_STUNTZI (TAG_USER | ( MUISERIALNR_STUNTZI << 16))
-
- #define MUIM_Class4_Update (TAGBASE_STUNTZI | 0x0001)
- struct MUIP_Class4_Update
- {
- ULONG id;
- LONG percent;
- };
-
- #define MUIM_Class4_Calc (TAGBASE_STUNTZI | 0x0002)
- struct MUIP_Class4_Calc
- {
- ULONG id;
- struct FractalDesc *fd;
- };
-
- #define MUIA_Class4_Percent (TAGBASE_STUNTZI | 0x0003)
-
-
- #define STC_START 0
- #define STC_STOP 1
-
-
- /* Instance Data for the fractal class */
-
- struct Data
- {
- struct SignalSemaphore sema; /* data item protection */
- Object *app; /* pointer to application */
- Object *self; /* pointer to ourselves */
- struct SubTask *subtask; /* our sub task */
- struct RastPort rp; /* rastport for the sub task */
- BYTE *udlines; /* line update flags array */
- };
-
-
-
-
- /**************************************************************/
- /* Functions for easy and secure spawning/killing of subtasks */
- /**************************************************************/
-
- struct SubTaskMsg
- {
- struct Message stm_Message;
- WORD stm_Command;
- APTR stm_Parameter;
- LONG stm_Result;
- };
-
- struct SubTask
- {
- struct Task *st_Task; /* sub task pointer */
- struct MsgPort *st_Port; /* allocated by sub task */
- struct MsgPort *st_Reply; /* allocated by main task */
- APTR st_Data; /* more initial data to pass to the sub task */
- struct SubTaskMsg st_Message; /* Message buffer */
- };
-
- #define STC_STARTUP -2
- #define STC_SHUTDOWN -1
-
- LONG SendSubTaskMsg(struct SubTask *st,WORD command,APTR params)
- {
- st->st_Message.stm_Message.mn_ReplyPort = st->st_Reply;
- st->st_Message.stm_Message.mn_Length = sizeof(struct SubTaskMsg);
- st->st_Message.stm_Command = command;
- st->st_Message.stm_Parameter = params;
- st->st_Message.stm_Result = 0;
-
- PutMsg(command==STC_STARTUP ? &((struct Process *)st->st_Task)->pr_MsgPort : st->st_Port,(struct Message *)&st->st_Message);
- WaitPort(st->st_Reply);
- GetMsg(st->st_Reply);
-
- return(st->st_Message.stm_Result);
- }
-
- struct SubTask *SpawnSubTask(char *name,VOID (*func)(VOID),APTR data)
- {
- struct SubTask *st;
-
- if (st = AllocVec(sizeof(struct SubTask),MEMF_PUBLIC|MEMF_CLEAR))
- {
- if (st->st_Reply = CreateMsgPort())
- {
- st->st_Data = data;
-
- if (st->st_Task = (struct Task *)CreateNewProcTags(NP_Entry,func,NP_Name,name,TAG_DONE))
- {
- if (SendSubTaskMsg(st,STC_STARTUP,st))
- {
- return(st);
- }
- }
- DeleteMsgPort(st->st_Reply);
- }
- FreeVec(st);
- }
- return(NULL);
- }
-
- VOID KillSubTask(struct SubTask *st)
- {
- SendSubTaskMsg(st,STC_SHUTDOWN,st);
- DeleteMsgPort(st->st_Reply);
- FreeVec(st);
- }
-
- /* the following functions are called from the sub task */
-
- VOID ExitSubTask(struct SubTask *st,struct SubTaskMsg *stm)
- {
- /*
- ** We reply after a Forbid() to make sure we're really gone
- ** when the main task continues.
- */
-
- if (st->st_Port)
- DeleteMsgPort(st->st_Port);
-
- Forbid();
- stm->stm_Result = FALSE;
- ReplyMsg((struct Message *)stm);
- }
-
- struct SubTask *InitSubTask(void)
- {
- struct Task *me = FindTask(NULL);
- struct SubTask *st;
- struct SubTaskMsg *stm;
-
- /*
- ** Wait for our startup message from the SpawnSubTask() function.
- */
-
- WaitPort(&((struct Process *)me)->pr_MsgPort);
- stm = (struct SubTaskMsg *)GetMsg(&((struct Process *)me)->pr_MsgPort);
- st = (struct SubTask *)stm->stm_Parameter;
-
- if (st->st_Port = CreateMsgPort())
- {
- /*
- ** Reply startup message, everything ok.
- ** Note that if the initialization fails, the code falls
- ** through and replies the startup message with a stm_Result
- ** of 0 after a Forbid(). This tells SpawnSubTask() that the
- ** sub task failed to run.
- */
-
- stm->stm_Result = TRUE;
- ReplyMsg((struct Message *)stm);
- return(st);
- }
- else
- {
- ExitSubTask(st,stm);
- return(NULL);
- }
- }
-
-
- /*******************************************************/
- /* Subtask which does all the time-consuming rendering */
- /*******************************************************/
-
- VOID __asm __saveds RenderFunc(VOID)
- {
- struct SubTask *st;
- struct Library *GfxBase;
-
- if (GfxBase = OpenLibrary("graphics.library",37)) /* dont share library pointers! */
- {
- if (st = InitSubTask())
- {
- struct Data *data = st->st_Data;
- BOOL running = TRUE;
- BOOL worktodo = FALSE;
- LONG x,y;
- struct SubTaskMsg *stm;
-
- /*
- ** after the sub task is up and running, we go into
- ** a loop and process the messages from the main task.
- */
-
- for (;;)
- {
- float left,top,right,bottom;
-
- while (stm = (struct SubTaskMsg *)GetMsg(st->st_Port))
- {
- switch (stm->stm_Command)
- {
- case STC_SHUTDOWN:
- /*
- ** This is the shutdown message from KillSubTask().
- */
- running = FALSE;
- break;
-
- case STC_START:
- /*
- ** we received a start message with a fractal description.
- ** clear the rastport and the line update array and start
- ** rendering.
- */
- SetRast(&data->rp,1);
- memset(data->udlines,0,FRACTALHEIGHT);
- left = ((struct FractalDesc *)stm->stm_Parameter)->left ;
- top = ((struct FractalDesc *)stm->stm_Parameter)->top ;
- right = ((struct FractalDesc *)stm->stm_Parameter)->right ;
- bottom = ((struct FractalDesc *)stm->stm_Parameter)->bottom;
- y = 0;
- worktodo = TRUE;
- break;
-
- case STC_STOP:
- /* this message is not used in this example */
- worktodo = FALSE;
- break;
- }
-
- /*
- ** If we received a shutdown message, we do not reply it
- ** immediately. First, we need to free our resources.
- */
- if (!running) break;
-
- ReplyMsg((struct Message *)stm);
- }
-
- if (!running) break;
-
- if (worktodo)
- {
- /* if there is work to do, i.e. if the fractal is not
- ** finished yet, we calculate the next line and draw
- ** it to the offscreen rastport.
- */
-
- for (x=0;x<FRACTALWIDTH;x++)
- {
- float zr = 0.0;
- float zi = 0.0;
- float cr = left + x * (right - left) / FRACTALWIDTH;
- float ci = top + y * (bottom - top) / FRACTALHEIGHT;
- float rr = zr * zr;
- float ii = zi * zi;
- int counter;
-
- for (counter=0;counter<MaxIterations;counter++)
- {
- zi = ci + zr * zi * 2;
- zr = cr + rr - ii;
- rr = zr * zr;
- ii = zi * zi;
-
- if (rr + ii >= 4.0)
- {
- /*
- ** set the pixel in the offscreen rastport.
- ** this demo is kind of dirty, as it does no
- ** nice color allocation and palette stuff.
- ** dont be so dirty in your own programs! :-)
- */
-
- SetAPen(&data->rp,1+counter);
- WritePixel(&data->rp,x,y);
- break;
- }
-
- if (++counter==MaxIterations)
- {
- break;
- }
- }
- }
-
- /*
- ** after the line is finished, we set the corresponding
- ** flag in the line update array to FALSE. This shows the
- ** main task that this line needs to be redrawn the next
- ** time it gets the chance.
- */
-
- ObtainSemaphore(&data->sema);
- data->udlines[y] = FALSE;
- if (data->app)
- {
- /*
- ** if our class is attached to an application, we send ourselves
- ** an update method. Note that because we are in a separate task,
- ** we cannot send this method directly but instead have to use
- ** the MUIM_Application_PushMethod call. This is the only method
- ** that you may send to a MUI object from a separate task. What it
- ** does is to copy the method to a private buffer and wait until
- ** the next time the main task calls the input method. Then, our
- ** update method will be executed under the main tasks context.
- **
- ** If our class is not attached to an application
- ** (i.e. we are outside of MUIM_Setup/MUIM_Cleanup), there is
- ** nobody who could render something anyway so we just skip
- ** the update method and continue to render in our private
- ** buffer.
- */
-
- DoMethod(data->app,MUIM_Application_PushMethod,data->self,2,MUIM_Class4_Update,100*(y+1)/FRACTALHEIGHT);
- }
- ReleaseSemaphore(&data->sema);
-
- if (++y==FRACTALHEIGHT)
- {
- /* check if we are finished to draw our fractal */
- worktodo = FALSE;
- }
-
- /* Since we are very busy working, we do not Wait() for signals. */
- }
- else
- {
- /* We have nothing to do, just sit quietly and wait for something to happen */
- WaitPort(st->st_Port);
- }
- }
- ExitSubTask(st,stm);
- }
- CloseLibrary(GfxBase);
- }
- }
-
-
-
- /***************************************************************************/
- /* Here is the beginning of our new class... */
- /***************************************************************************/
-
-
- SAVEDS ULONG mNew(struct IClass *cl,Object *obj,Msg msg)
- {
- struct Data *data;
-
- if (!(obj = (Object *)DoSuperMethodA(cl,obj,msg)))
- return(0);
-
- data = INST_DATA(cl,obj);
-
- /* store a pointer to ourselves so the subtask knows about us */
- data->self = obj;
-
- /*
- ** initialization and allocation of data structures.
- ** note that if something fails here, we *must* do a
- ** CoerceMethod(cl,obj,OM_DISPOSE) to give ourselves
- ** (and MUI!) a chance to clean up.
- */
-
- InitSemaphore(&data->sema);
- InitRastPort(&data->rp);
-
- if (data->udlines = AllocVec(FRACTALHEIGHT,MEMF_CLEAR))
- {
- if (data->rp.BitMap = AllocBitMap(FRACTALWIDTH,FRACTALHEIGHT,8,BMF_CLEAR,NULL))
- {
- SetRast(&data->rp,1);
-
- /* the following call starts the sub task */
-
- if (data->subtask = SpawnSubTask("Class4-Render-Task",RenderFunc,data))
- {
- SetTaskPri(data->subtask->st_Task,-1);
- return((ULONG)obj);
- }
- }
- }
-
- CoerceMethod(cl,obj,OM_DISPOSE);
- return(0);
- }
-
-
-
- SAVEDS ULONG mDispose(struct IClass *cl,Object *obj,Msg msg)
- {
- struct Data *data = INST_DATA(cl,obj);
-
- if (data->subtask)
- KillSubTask(data->subtask);
-
- if (data->rp.BitMap)
- FreeBitMap(data->rp.BitMap);
-
- if (data->udlines)
- FreeVec(data->udlines);
-
- return(DoSuperMethodA(cl,obj,msg));
- }
-
-
-
- /*
- ** AskMinMax method will be called before the window is opened
- ** and before layout takes place. We need to tell MUI the
- ** minimum, maximum and default size of our object.
- */
-
- SAVEDS ULONG mAskMinMax(struct IClass *cl,Object *obj,struct MUIP_AskMinMax *msg)
- {
- /*
- ** let our superclass first fill in what it thinks about sizes.
- ** this will e.g. add the size of frame and inner spacing.
- */
-
- DoSuperMethodA(cl,obj,msg);
-
- /*
- ** now add the values specific to our object. note that we
- ** indeed need to *add* these values, not just set them!
- */
-
- msg->MinMaxInfo->MinWidth += 10;
- msg->MinMaxInfo->DefWidth += 100;
- msg->MinMaxInfo->MaxWidth += FRACTALWIDTH;
-
- msg->MinMaxInfo->MinHeight += 10;
- msg->MinMaxInfo->DefHeight += 100;
- msg->MinMaxInfo->MaxHeight += FRACTALHEIGHT;
-
- return(0);
- }
-
-
-
- /*
- ** Draw method is called whenever MUI feels we should render
- ** our object. This usually happens after layout is finished
- ** or when we need to refresh in a simplerefresh window.
- ** Note: You may only render within the rectangle
- ** _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj).
- */
-
- SAVEDS ULONG mDraw(struct IClass *cl,Object *obj,struct MUIP_Draw *msg)
- {
- struct Data *data = INST_DATA(cl,obj);
-
- /*
- ** let our superclass draw itself first, area class would
- ** e.g. draw the frame and clear the whole region. What
- ** it does exactly depends on msg->flags.
- **
- ** Note: You *must* call the super method prior to do
- ** anything else, otherwise msg->flags will not be set
- ** properly!
- */
-
- DoSuperMethodA(cl,obj,msg);
-
- if (msg->flags & MADF_DRAWUPDATE)
- {
- /*
- ** This flag indicates that we were called from our
- ** update method. We needn't render the complete
- ** image, we only need to update the lines that
- ** were changed. So what we do is to browse through
- ** the line flag array and blit each changed line
- ** from the offscreen buffer into the display.
- ** We could do a better and more efficient job
- ** by collecting subsequent changed lines to blit
- ** larger rectangles, but hey... this is only a demo! :-)
- */
-
- int l;
-
- /*
- ** note the usage of semaphores to protect access
- ** to variables use by both tasks.
- */
-
- ObtainSemaphore(&data->sema);
-
- for (l=0;l<_mheight(obj);l++)
- {
- if (!data->udlines[l])
- {
- /*
- ** once we copied the line, we set the corresponding line flag
- ** to indicate that this line is uptodate and does not need
- ** to be redrawn the next time. When our sub task gets the message
- ** to calculate a new fractal, it will reset the flag to FALSE again.
- */
-
- BltBitMapRastPort(data->rp.BitMap,0,l,_rp(obj),_mleft(obj),_mtop(obj)+l,_mwidth(obj),1,0xc0);
- data->udlines[l] = TRUE;
- }
- }
-
- ReleaseSemaphore(&data->sema);
- }
- else if (msg->flags & MADF_DRAWOBJECT)
- {
- /*
- ** we were called directly from MUI because the window needs refresh.
- ** no need to care about our line array here, we just copy the complete
- ** offscreen buffer to our display.
- */
-
- ObtainSemaphore(&data->sema);
- BltBitMapRastPort(data->rp.BitMap,0,0,_rp(obj),_mleft(obj),_mtop(obj),_mwidth(obj),_mheight(obj),0xc0);
- ReleaseSemaphore(&data->sema);
- }
-
- return(0);
- }
-
-
- SAVEDS ULONG mSetup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
- {
- struct Data *data = INST_DATA(cl,obj);
-
- if (!(DoSuperMethodA(cl,obj,msg)))
- return(FALSE);
-
- /*
- ** set a pointer to our application in our instance data.
- ** this indicates the sub task that we should be notified
- ** when a new line is calculated.
- */
-
- ObtainSemaphore(&data->sema);
- get(obj,MUIA_ApplicationObject,&data->app);
- ReleaseSemaphore(&data->sema);
-
- return(TRUE);
- }
-
-
- SAVEDS ULONG mCleanup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
- {
- struct Data *data = INST_DATA(cl,obj);
-
- ObtainSemaphore(&data->sema);
- data->app = NULL;
- ReleaseSemaphore(&data->sema);
-
- return(DoSuperMethodA(cl,obj,msg));
- }
-
-
- /*
- ** a simple method that sends a START msg with
- ** fractal description packet to the sub task.
- */
-
- SAVEDS ULONG mCalc(struct IClass *cl,Object *obj,struct MUIP_Class4_Calc *msg)
- {
- struct Data *data = INST_DATA(cl,obj);
- SendSubTaskMsg(data->subtask,STC_START,msg->fd);
- return(0);
- }
-
-
- /*
- ** thats the method that is called through MUIM_Application_PushMethod
- ** from the subtask.
- */
-
- SAVEDS ULONG mUpdate(struct IClass *cl,Object *obj,struct MUIP_Class4_Update *msg)
- {
- /* Tell MUI to redraw our object. Set the update flag
- ** so we know that only the changed lines are subject
- ** to render.
- */
-
- MUI_Redraw(obj,MADF_DRAWUPDATE);
-
- /*
- ** Also the the percentage attribute. The class itself doesnt
- ** have any use for this, but if we set it, its possible
- ** for other objects (e.g. a gauge) to receive notifications
- */
-
- set(obj,MUIA_Class4_Percent,msg->percent);
-
- return(0);
- }
-
-
- /*
- ** Here comes the dispatcher for our custom class.
- ** Unknown/unused methods are passed to the superclass immediately.
- */
-
- SAVEDS ASM ULONG MyDispatcher(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg)
- {
- switch (msg->MethodID)
- {
- case OM_NEW : return(mNew (cl,obj,(APTR)msg));
- case OM_DISPOSE : return(mDispose (cl,obj,(APTR)msg));
- case MUIM_AskMinMax : return(mAskMinMax(cl,obj,(APTR)msg));
- case MUIM_Draw : return(mDraw (cl,obj,(APTR)msg));
- case MUIM_Setup : return(mSetup (cl,obj,(APTR)msg));
- case MUIM_Cleanup : return(mCleanup (cl,obj,(APTR)msg));
- case MUIM_Class4_Update: return(mUpdate (cl,obj,(APTR)msg));
- case MUIM_Class4_Calc : return(mCalc (cl,obj,(APTR)msg));
- }
-
- return(DoSuperMethodA(cl,obj,msg));
- }
-
-
-
- /****************************************************************************/
- /* Misc Help Functions */
- /****************************************************************************/
-
- LONG xget(Object *obj,ULONG attribute)
- {
- LONG x;
- get(obj,attribute,&x);
- return(x);
- }
-
- char *getstr(Object *obj)
- {
- return((char *)xget(obj,MUIA_String_Contents));
- }
-
-
-
- /***************************************************************************/
- /* Thats all there is about it. Now lets see how things are used... */
- /***************************************************************************/
-
- int main(int argc,char *argv[])
- {
- Object *app,*window,*MyObj;
- Object *strleft,*strtop,*strright,*strbottom,*start,*gauge;
- struct MUI_CustomClass *mcc;
- ULONG signals;
- BOOL running = TRUE;
-
- if (((struct Library *)SysBase)->lib_Version < 39)
- {
- fprintf(stderr,"runs only with V39 and up\n");
- exit(20);
- }
-
- init();
-
- /* Create the new custom class with a call to MUI_CreateCustomClass(). */
- /* Caution: This function returns not a struct IClass, but a */
- /* struct MUI_CustomClass which contains a struct IClass to be */
- /* used with NewObject() calls. */
- /* Note well: MUI creates the dispatcher hook for you, you may */
- /* *not* use its h_Data field! If you need custom data, use the */
- /* cl_UserData of the IClass structure! */
-
- if (!(mcc = MUI_CreateCustomClass(NULL,MUIC_Area,NULL,sizeof(struct Data),MyDispatcher)))
- fail(NULL,"Could not create custom class.");
-
- app = ApplicationObject,
- MUIA_Application_Title , "Class4",
- MUIA_Application_Version , "$VER: Class4 19.5 (12.02.97)",
- MUIA_Application_Copyright , "©1993, Stefan Stuntz",
- MUIA_Application_Author , "Stefan Stuntz",
- MUIA_Application_Description, "Demonstrate rendering from sub tasks.",
- MUIA_Application_Base , "Class4",
-
- SubWindow, window = WindowObject,
- MUIA_Window_Title, "Subtask rendering",
- MUIA_Window_ID , MAKE_ID('C','L','S','4'),
- WindowContents, VGroup,
-
- Child, HGroup, GroupSpacing(8),
- Child, ColGroup(2),
- Child, Label2("_Left:" ), Child, strleft = MUI_MakeObject(MUIO_String,"_L",30),
- Child, Label2("_Right:" ), Child, strright = MUI_MakeObject(MUIO_String,"_R",30),
- End,
- Child, ColGroup(2),
- Child, Label2("_Top:" ), Child, strtop = MUI_MakeObject(MUIO_String,"_T",30),
- Child, Label2("_Bottom:"), Child, strbottom = MUI_MakeObject(MUIO_String,"_B",30),
- End,
- Child, MUI_MakeObject(MUIO_VBar,2),
- Child, start = VGroup,
- GroupSpacing(0),
- MUIA_Weight, 0,
- ButtonFrame,
- MUIA_InputMode , MUIV_InputMode_RelVerify,
- MUIA_Background, MUII_ButtonBack,
- Child, VSpace(0),
- Child, TextObject, MUIA_Text_Contents, "\33c Start ", End,
- Child, VSpace(0),
- End,
- End,
-
- Child, gauge = GaugeObject,
- GaugeFrame,
- MUIA_Gauge_Horiz, TRUE,
- MUIA_Gauge_Max, 100,
- MUIA_FixHeight, 8,
- End,
-
- Child, MyObj = NewObject(mcc->mcc_Class,NULL,
- TextFrame,
- MUIA_Background, MUII_BACKGROUND,
- TAG_DONE),
-
- End,
- End,
- End;
-
- if (!app)
- fail(app,"Failed to create Application.");
-
- set(window,MUIA_Window_DefaultObject, MyObj);
-
- DoMethod(window,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,
- app,2,MUIM_Application_ReturnID,MUIV_Application_ReturnID_Quit);
-
- DoMethod(start,MUIM_Notify,MUIA_Pressed,FALSE,
- app,2,MUIM_Application_ReturnID,1);
-
- DoMethod(MyObj,MUIM_Notify,MUIA_Class4_Percent,MUIV_EveryTime,
- gauge,3,MUIM_Set,MUIA_Gauge_Current,MUIV_TriggerValue);
-
- set(strleft ,MUIA_String_Contents,"-2.0");
- set(strright ,MUIA_String_Contents,"1.0");
- set(strtop ,MUIA_String_Contents,"1.5");
- set(strbottom,MUIA_String_Contents,"-1.5");
-
- /*
- ** Input loop...
- */
-
- set(window,MUIA_Window_Open,TRUE);
-
- while (running)
- {
- switch (DoMethod(app,MUIM_Application_Input,&signals))
- {
- case MUIV_Application_ReturnID_Quit:
- running = FALSE;
- break;
-
- case 1:
- {
- struct FractalDesc fd;
-
- fd.left = atof(getstr(strleft ));
- fd.right = atof(getstr(strright ));
- fd.top = atof(getstr(strtop ));
- fd.bottom = atof(getstr(strbottom));
-
- if (fd.right > fd.left && fd.top > fd.bottom)
- DoMethod(MyObj,MUIM_Class4_Calc,&fd);
- else
- DisplayBeep(0);
- }
- break;
- }
-
- if (running && signals) Wait(signals);
- }
-
- set(window,MUIA_Window_Open,FALSE);
-
-
- /*
- ** Shut down...
- */
-
- MUI_DisposeObject(app); /* dispose all objects. */
- MUI_DeleteCustomClass(mcc); /* delete the custom class. */
- fail(NULL,NULL); /* exit, app is already disposed. */
- }
-