page%swap@Sun.COM (Bob Page) (05/11/89)
Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn) Posting-number: Volume 89, Issue 126 Archive-name: libraries/minrexx04.1 This is the latest version of Tom Rokicki's code to show people how to add a 7-function ARexx port to their own code. [uuencoded executable included. ..bob] # This is a shell archive. # Remove anything above and including the cut line. # Then run the rest of the file through 'sh'. # Unpacked files will be owned by you and have default permissions. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: SHell ARchive # Run the following text through 'sh' to create: # README # freedraw.c # minrexx.c # minrexx.h # Makefile # freeedraw.uu # aspline.fd # bspline.fd # sample.fd # saspline.fd # sbspline.fd # version.fd # This is archive 1 of a 1-part kit. # This archive created: Thu May 11 00:22:53 1989 echo "extracting README" sed 's/^X//' << \SHAR_EOF > README XHere's "minrexx", a simple ARexx interface which can be easily patched into Xalmost any program, and an example from Fish Disk 1, where I add an ARexx Xport to freedraw. X XFor documentation on Minrexx, refer to the .c and .h file. X XFor documentation on the Freedraw port, see the portions of freedraw.c Xdelimited by "#ifdef TALKTOREXX". X XEnjoy! SHAR_EOF echo "extracting freedraw.c" sed 's/^X//' << \SHAR_EOF > freedraw.c X#ifdef TALKTOREXX X/* X * This program is an example of how to add a Rexx port to a given X * program. I thought it particularly appropriate to grab a program X * off Fred Fish Disk 1. All the REXX stuff is bracketed by `ifdef X * TALKTOREXX', so you can identify it easily. If you compile with X * TALKTOREXX unset, you will get the default program with no REXX X * port. X * X * The REXX port on this program adds another 3K to the executable X * size, but a lot of functionality comes with that. You can draw X * from Rexx, spawn macros from Rexx, etc. But go to the next X * TALKTOREXX for more information. X * X * To run a rexx macro on startup, simply give that rexx macro as X * part of the command line, as in X * X * freedraw sample X * X * or X * X * freedraw bspline 20 100 20 20 280 20 280 100 X * X * All modifications are by Radical Eye Software, and all modifications X * are placed in the public domain. X */ X#endif X/************************************************************************/ X/*** FreeDraw - PD graphics for Amiga ***/ X/*** ***/ X/*** This is an extremely simple graphics editor which works in ***/ X/*** the windowing environment of the Amiga. It is very limited ***/ X/*** in features, but I hope to add a lot more, and I would be ***/ X/*** happy to receive assistance from anyone who wants to give it. ***/ X/*** The basic idea of this program is to provide some minimal ***/ X/*** image editing functions which can be used to develop images ***/ X/*** for other programs. I know there will be a lot of Paint type ***/ X/*** type programs avaialable soon, but what are we supposed to use ***/ X/*** now? The most important features to add now will probably be ***/ X/*** those related to "cut and paste", disk srtorage and retrieval, ***/ X/*** and "single-pixel" editing like Mac's "fatbits". ***/ X/*** I intend to use the IFF standard for the image storage and ***/ X/*** retrieval and will be coding a "Files" menu soon. The work ***/ X/*** required for "cut and paste" should be almost trivial, but I ***/ X/*** still may not get to it for a while. Fatbits editing from one ***/ X/*** window to another involves some manipulations of the RastPorts ***/ X/*** which still elude me, as I have only recently begun to use the ***/ X/*** Amiga and don't yet understand some important details of these ***/ X/*** structures. This would be a great item for one of the genius ***/ X/*** members of the Amiga programming community to provide. ***/ X/*** There are only two menu topics in this version, so using it ***/ X/*** is really quite easy. Boxes are not allowed to be drawn in ***/ X/*** areas outside of the window where border gadgets are located, ***/ X/*** and the pen-draw mode also clips to these same boundaries. If ***/ X/*** you have begun to draw a box by clicking the left button while ***/ X/*** the cursor is located in the FreeDraw window, then you can ***/ X/*** cancel that box by clicking the right button. In the pen mode ***/ X/*** pressing and holding the left button will draw. Colors are ***/ X/*** selected by simply releasing the menu button over the desired ***/ X/*** color in the Color menu. The erase feature always clears the ***/ X/*** window to the currently selected color. ***/ X/*** This is no gem of programming style, but you're getting it ***/ X/*** for the right price so be patient with its design flaws. New ***/ X/*** versions will appear here on BIX as soon as I can get them in ***/ X/*** shape for release. I apologize to anyone who objects to my ***/ X/*** lack of coding grace, but I just want to get the project off ***/ X/*** the ground, and improvements will be forthcoming. There are ***/ X/*** a lot of comments, but I didn't know what needed to be made ***/ X/*** clear so I just commented everything. ***/ X/*** ***/ X/*** If you like the idea of a PD graphics program and would be ***/ X/*** interested in doing some development work, then please write ***/ X/*** me at the address listed below, or call if you prefer. I do ***/ X/*** want to know if there is any interest in such a project, so ***/ X/*** I will be glad to discuss any ideas you might have. Also, as ***/ X/*** I do not currently use CompuServe or any other major nets, I ***/ X/*** would appreciate if someone would post this listing there. ***/ X/*** I hope somebody enjoys this. Have Fun. ***/ X/*** Rick Ross 11/14/85 ***/ X/*** ***/ X/*** My address: ***/ X/*** Richard M. Ross, Jr. ***/ X/*** Eidetic Imaging ***/ X/*** 740 N. 22nd Street ***/ X/*** Philadelphia, PA 19130 ***/ X/*** ***/ X/*** Phone - (215) 236-7388 ***/ X/************************************************************************/ Xchar *VERSION = "Freedraw 0.01 by Richard M. Ross" ; X/* compiler directives to fetch the necessary header files */ X X#include <exec/types.h> X#include <exec/exec.h> X#include <intuition/intuition.h> X#include <intuition/intuitionbase.h> X#include <graphics/gfx.h> X#include <graphics/regions.h> X#include <graphics/copper.h> X#include <graphics/gels.h> X#include <graphics/gfxbase.h> X#include <devices/keymap.h> X#include <hardware/blit.h> X X/* These definitions are used by intuition for X * calls to OpenLibrary() in order to ensure X * that an appropriate ROM revision is X * available. X */ X X#define INTUITION_REV 1L X#define GRAPHICS_REV 1L X X/* Intuition always wants to see these declarations */ Xstruct IntuitionBase *IntuitionBase; Xstruct GfxBase *GfxBase; X X/* This is the Window structure declaration. X * Nothing fancy is going on here, but note X * the Flags and IDCMPFlags members of the X * structure define which messages will be X * sent by Intuition. I haven't used them all X * and if you want to change the settings you X * should probably do it her instead of using X * ModifyIDCMP later. X */ X Xstruct NewWindow NewWindow = { X 10, X 10, X 600, X 180, X 0, X 1, X CLOSEWINDOW | MOUSEMOVE | MOUSEBUTTONS | MENUPICK X | NEWSIZE | INACTIVEWINDOW, X WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG X | WINDOWDEPTH | WINDOWSIZING | REPORTMOUSE, X NULL, X NULL, X (UBYTE *)"AMIGA FreeDraw 0.01", X NULL, X NULL, X 100, 35, X -1, -1, X WBENCHSCREEN, X}; X#ifdef TALKTOREXX X/* X * We need our include file. X */ X#include "minrexx.h" X/* X * These are the REXX functions defined at the bottom of the file. X */ Xvoid rexxcolor(), rexxbox(), rexxfbox(), rexxline(), rexxtofront(), X rexxtoback(), rexxexit(), rexxversion(), rexxspawn() ; Xint disp() ; X/* X * Here is our command association list. Note that in this case, X * we are setting the userdata field to be a function to call. X * Dispatch will still take place through disp(), so common head X * and tail stuff can go there. X * X * Commands are all lower case, so we match either upper or lower. X * (This is a requirement of minrexx.) X */ Xstruct rexxCommandList rcl[] = { X { "color", (APTR)&rexxcolor }, X { "box", (APTR)&rexxbox }, X { "fbox", (APTR)&rexxfbox }, X { "line", (APTR)&rexxline }, X { "tofront", (APTR)&rexxtofront }, X { "toback", (APTR)&rexxtoback }, X { "exit", (APTR)&rexxexit }, X { "version", (APTR)&rexxversion }, X { "spawn", (APTR)&rexxspawn }, X { NULL, NULL } } ; X#endif X/*******************************************************************/ X/* DrawBox - Simple routine to draw an unfilled rectangle */ X/* It accepts the coordinates of the top-left and lower-right */ X/* points of the rectangle, a pointer to the Window structure, */ X/* and the color in which to render the rectangle. The current */ X/* FgPen color of the window is preserved thru the call. No */ X/* clipping is done. */ X/*******************************************************************/ Xvoid DrawBox( tlx, tly, brx, bry, window, color ) XSHORT tlx, tly; /* top-left x,y coordinates */ XSHORT brx, bry; /* lower-right x,y coordinates */ Xstruct Window *window; /* pointer to target window */ XBYTE color; /* color to use for render */ X { X BYTE OldColor = window->RPort->FgPen; /* save window's FgPen */ X X SetAPen( window->RPort, (long)color ); /* set draw color for box */ X Move(window->RPort, (long)tlx, (long)tly); /* move to top-left point */ X Draw(window->RPort, (long)brx, (long)tly); /* and draw to each of the */ X Draw(window->RPort, (long)brx, (long)bry); /* four corners of the box */ X Draw(window->RPort, (long)tlx, (long)bry); X Draw(window->RPort, (long)tlx, (long)tly); X SetAPen( window->RPort, (long)OldColor ); /* restore old FgPen */ X } X X X/*********************************************************/ X/* Color Select Menu */ X/* */ X/* This is where the menu for color selection is */ X/* defined. It should be flexible enough to allow for */ X/* increased palette sizes, but this version is only */ X/* for the 4-color mode of the WorkBench screen. */ X/*********************************************************/ X X/* A few definitions are needed here. X * Note that MAXPAL should be increased X * to allow for palette larger than X * four colors. X */ X#define ITEMSTUFF (ITEMENABLED | HIGHBOX) X#define CW 40 X#define CH 25 X#define MAXPAL 4 X X/* declare enough storage for required X * number of menu items and associated X * images. This menu will be using X * graphics renditions of menu items, X * so the Image structures must be X * declared. This menu is modeled after X * the one found in the IconEd source. X */ Xstruct MenuItem coloritem[MAXPAL]; Xstruct Image colorimage[MAXPAL]; X X/* array of palette sizes to correspond with X * depth of window in bit-planes X */ XSHORT palette[] = { 2, 4, 8, 16, 32 }; X X X/*****************************************************************/ X/* The following function initializes the structure arrays */ X/* needed to provide the Color menu topic. */ X/*****************************************************************/ XInitColorItems( depth ) XSHORT depth; /* number of bit-planes in window */ X { X SHORT n, colors; X X colors = palette[depth-1]; X for( n=0; n<colors; n++ ) /* loop for max number of items */ X { X coloritem[n].NextItem = &coloritem[n+1]; X coloritem[n].ItemFill = (APTR)&colorimage[n]; X /* the next two items might be changed for X * when bit-planes is greater than 2 X */ X coloritem[n].LeftEdge = 2 + CW * (n % 4); X coloritem[n].TopEdge = CH * (n / 4); X coloritem[n].Width = CW; X coloritem[n].Height = CH; X coloritem[n].Flags = ITEMSTUFF; X coloritem[n].MutualExclude = 0; X coloritem[n].SelectFill = NULL; X coloritem[n].Command = 0; X coloritem[n].SubItem = NULL; X coloritem[n].NextSelect = 0; X X colorimage[n].LeftEdge = 1; X colorimage[n].TopEdge = 1; X colorimage[n].Width = CW-2; X colorimage[n].Height = CH-2; X colorimage[n].Depth = depth; X colorimage[n].ImageData = NULL; X colorimage[n].PlanePick = 0; X colorimage[n].PlaneOnOff = n; X } X coloritem[colors-1].NextItem = NULL; /* needed for last item in list */ X return( 0 ); X } X X X/*****************************************************/ X/* Draw Mode Menu */ X/* */ X/* Here are the code and data declarations for */ X/* the DrawMode menu. Current choices are limited */ X/* to Erase, Filled Box, Hollow Box, and PenDraw. */ X/*****************************************************/ X X/* define maximum number of menu items */ X#define DMODEMAX 4 X X/* declare storage space for menu items and X * their associated IntuiText structures X */ Xstruct MenuItem DModeItem[DMODEMAX]; Xstruct IntuiText DModeText[DMODEMAX]; X X/*****************************************************************/ X/* The following function initializes the structure arrays */ X/* needed to provide the DrawMode menu topic. */ X/*****************************************************************/ XInitDModeItems() X { X short n; X X /* initialize each meu item and IntuiText with loop */ X for( n=0; n<DMODEMAX; n++ ) X { X DModeItem[n].NextItem = &DModeItem[n+1]; X DModeItem[n].LeftEdge = 0; X DModeItem[n].TopEdge = 10 * n; X DModeItem[n].Width = 112; X DModeItem[n].Height = 10; X DModeItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX; X DModeItem[n].MutualExclude = 0; X DModeItem[n].ItemFill = (APTR)&DModeText[n]; X DModeItem[n].SelectFill = NULL; X DModeItem[n].Command = 0; X DModeItem[n].SubItem = NULL; X DModeItem[n].NextSelect = 0; X X DModeText[n].FrontPen = 0; X DModeText[n].BackPen = 1; X DModeText[n].DrawMode = JAM2; /* render in fore and background */ X DModeText[n].LeftEdge = 0; X DModeText[n].TopEdge = 1; X DModeText[n].ITextFont = NULL; X DModeText[n].NextText = NULL; X } X DModeItem[DMODEMAX-1].NextItem = NULL; X X /* initialize text for specific menu items */ X DModeText[0].IText = (UBYTE *)"Erase All"; X DModeText[1].IText = (UBYTE *)"Hollow Box"; X DModeText[2].IText = (UBYTE *)"Filled Box"; X DModeText[3].IText = (UBYTE *)"Pen Draw"; X X return( 0 ); X } X X X/***************************************************/ X/* Menu Definition */ X/* */ X/* This section of code is where the simple */ X/* menu definition goes. For now it supports */ X/* only Color and Drawmode selection, but new */ X/* choices can easily be added by creating */ X/* structures and initializations functions */ X/* similar to those provided above. */ X/***************************************************/ X X/* current number of available menu topics */ X#define MAXMENU 2 X X/* declaration of menu structure array for X * number of current topics. Intuition X * will use the address of this array to X * set and clear the menus associated with X * the window. X */ Xstruct Menu menu[MAXMENU]; X X/**********************************************************************/ X/* The following function initializes the Menu structure array with */ X/* appropriate values for our simple menu strip. Review the manual */ X/* if you need to know what each value means. */ X/**********************************************************************/ XInitMenu() X { X menu[0].NextMenu = &menu[1]; X menu[0].LeftEdge = 10; X menu[0].TopEdge = 0; X menu[0].Width = 50; X menu[0].Height = 10; X menu[0].Flags = MENUENABLED; X menu[0].MenuName = "Color"; /* text for menu-bar display */ X menu[0].FirstItem = &coloritem[0]; /* pointer to first item in list */ X X menu[1].NextMenu = NULL; X menu[1].LeftEdge = 65; X menu[1].TopEdge = 0; X menu[1].Width = 85; X menu[1].Height = 10; X menu[1].Flags = MENUENABLED; X menu[1].MenuName = "DrawMode"; /* text for menu-bar display */ X menu[1].FirstItem = &DModeItem[0]; /* pointer to first item in list */ X X return( 0 ); X } X X X/******************************************************/ X/* Main Program */ X/* */ X/* This is the main body of the program. */ X/******************************************************/ X Xstruct Window *Window; /* ptr to applications window */ XSHORT MinX, MinY, MaxX, MaxY; /* clipping boundary variables */ XSHORT KeepGoing = TRUE; /* main loop control value */ X Xmain(argc, argv) Xint argc ; Xchar *argv[] ; X { X struct Library *OpenLibrary() ; X struct Window *OpenWindow() ; X struct Message *GetMsg() ; X struct IntuiMessage *NewMessage; /* msg structure for GetMsg() */ X BYTE DrawColor = 1; /* initial drawing color */ X SHORT OldBRX = 30, OldBRY = 30; /* point coords used for boxes */ X SHORT TLX = 20, TLY = 20; /* initial top-left point coords */ X ULONG class; /* used in message monitor loop */ X USHORT code; /* used in message monitor loop */ X SHORT x, y, x1, y1, x2, y2; /* various coordinate variables */ X USHORT MenuNum, ItemNum; X /* The following is a set of declarations X * for a number of flag values used by the X * program. These would perhaps be better X * coded as a bit-field for all the flags, X * but I'm lazy, and this is easier. X */ X SHORT MouseMoved = FALSE; /* indicates new mouse position ready */ X SHORT ClipIt = FALSE; /* are new point coords out of bounds? */ X SHORT ClippedLast = FALSE; /* was last PenDraw operation clipped? */ X SHORT PenMode = FALSE; /* indicates PenDraw mode is set */ X SHORT PenDown = FALSE; /* if mouse moved, then should it draw? */ X SHORT RubberBox = FALSE; /* are we currently rubberbanding a box? */ X SHORT FilledBox = FALSE; /* should boxes be filled when drawn? */ X#ifdef TALKTOREXX X/* X * If we are talking to REXX, we need these two additional locals. X */ X long rexxbit ; X char firstcommand[256] ; X#endif X X /* attempt to Open Library to access Intuition */ X IntuitionBase = (struct IntuitionBase *) X OpenLibrary("intuition.library", INTUITION_REV); X if( IntuitionBase == NULL ) X exit(FALSE); X X /* attempt to OpenLibrary to access Graphics functions */ X GfxBase = (struct GfxBase *) X OpenLibrary("graphics.library",GRAPHICS_REV); X if( GfxBase == NULL ) X exit(FALSE); X X X /* Try to open new window for application */ X if(( Window = OpenWindow(&NewWindow) ) == NULL) X exit(FALSE); X X /* set initial clipping boundaries X * from the values found in the window X * structure for border dimensions X */ X MinX = Window->BorderLeft; X MinY = Window->BorderTop; X MaxX = Window->Width - Window->BorderRight - 1; X MaxY = Window->Height - Window->BorderBottom - 1; X X InitColorItems( 2 ); /* initialize Color menu arrays */ X InitDModeItems(); /* initialize DrawMode menu arrays */ X InitMenu(); /* initialize the menu structures */ X X /* Now, having initialized the various arrays X * of structures required for menu generation X * we can tell Intuition to make our menus X * available to the user when this window X * is active. X */ X SetMenuStrip( Window, &menu[0] ); X X /* set initial drw mode and color */ X SetDrMd( Window->RPort, JAM1 ); X SetAPen( Window->RPort, DrawColor ); X#ifdef TALKTOREXX X/* X * For rexx, we open up a Rexx port, and send out the first command, X * if there was one. We send it out asynchronously; no reason not to. X */ X rexxbit = upRexxPort("freedraw", rcl, "fd", &disp) ; X firstcommand[0] = 0 ; X for (x=1; x<argc; x++) { X strcat(firstcommand, argv[x]) ; X strcat(firstcommand, " ") ; X } X if (firstcommand[0]) { X asyncRexxCmd(firstcommand) ; X } X#endif X /* Everything the program needs is now X * initialized and put in place. The X * program enters the following loop X * and processes message continuously as X * they are received from Intuition. X * I guess this loop is the real workhorse X * of the program. By the way, the loop X * control variable KeepGoing remains TRUE X * until a CLOSEWINDOW message is received. X * At that point it goes FALSE, and the X * program cleans up and exits. X */ X while( KeepGoing ) X { X X /* stay here until a message is received from Intuition */ X#ifdef TALKTOREXX X/* X * If we're working with Rexx, we wait on the Rexx bit as well. X * Then, we handle any Rexx messages. X */ X Wait( (1L << Window->UserPort->mp_SigBit) | rexxbit); X dispRexxPort() ; X#else X Wait( 1L << Window->UserPort->mp_SigBit); X#endif X MouseMoved = FALSE; /* clear this flag each time thru loop */ X X /* since more than one message may be waiting X * a reply at this point, a loop is used to X * process all that have come in until no more X * are ready. Msg received is assigned to X * NewMessage from the GetMsg() function. This X * value will be NULL if no message is ready, X * and control passes out of the loop at that time X */ X while( NewMessage=(struct IntuiMessage *)GetMsg(Window->UserPort) ) X { X X /* copy some values from the message structure X * to variables used in the switch statements X * below X */ X class = NewMessage->Class; X code = NewMessage->Code; X x = Window->MouseX; X y = Window->MouseY; X X /* SIZEVERIFY is a very high priority message X * in our loop and requires some immediate X * servicing. Any outstanding draw operations X * are immediately cancelled, and the DrawMode X * is nulled. This prevents any attempts to X * render outside whatever new Window boundaries X * the user chooses. X * X * (not anymore, it don't. -tgr) X X if( class == SIZEVERIFY ) X { X PenDown = FALSE; X if( RubberBox ) X { X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor ); X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP ); X RubberBox = FALSE; X } X } X */ X X /* we have all the information needed from X * the message, so we can now safely reply X * to it without losing data X */ X ReplyMsg( NewMessage ); X X /* Examine point coords from message received X * and set the clipping flag if out of bounds. X * If user was drawing in PenMode when message X * was received, then the ClippedLast flag X * should also be set to indicate this to the X * next draw operation. X */ X if(ClipIt = ( x < MinX || x > MaxX || y < MinY || y > MaxY )) X if( PenDown ) X ClippedLast = TRUE; X X X /* enter switch on type of message received */ X switch( class ) X { X case MOUSEMOVE: X /* Don't really do anything with this one X * until any other, more important, messages X * are received and processed. X */ X MouseMoved = TRUE; X break; X X case NEWSIZE: X /* set new clipping boundaries */ X MinX = Window->BorderLeft; X MinY = Window->BorderTop; X MaxX = Window->Width - Window->BorderRight - 1; X MaxY = Window->Height - Window->BorderBottom - 1; X break; X X case CLOSEWINDOW: X /* User is ready to quit, so indicate X * that execution should terminate X * with next iteration of the loop. X */ X KeepGoing = FALSE; X break; X X case MOUSEBUTTONS: X /* A number of things could have happened X * here, and further examination of data X * received from message is needed to X * determine what action should be taken. X * The code variable holds important info X * about what actually caused the message X * to be sent in the first place. X */ X switch ( code ) X { X case SELECTUP: X /* User was holding down the left button X * and just released it. The PenMode X * flag variables are set accordingly. X * The pen can no longer be down, and X * ClippedLast is reset for next time. X */ X PenDown = ClippedLast = FALSE; X break; X X case SELECTDOWN: X /* User has pressed the left button, and X * several differnt actions may need to X * be taken. If the ClipIt value is TRUE, X * then no action should be taken at all. X */ X if( ClipIt ) X break; X X /* If user is currently in PenMode, then X * set up to draw when MOUSEMOVED messages X * are received until a subsequent SELECTUP X * message comes in. X */ X if( PenMode ) X { X PenDown = TRUE; X ClippedLast = FALSE; X X /* make sure to set appropriate mode */ X SetDrMd( Window->RPort, JAM1 ); X X /* and establish initial position to draw */ X Move( Window->RPort, (long)x, (long)y ); X break; X } X X /* If user is currently rubberbanding a box, X * then a SELECTDOWN message means it is time X * to stop rubberbanding and actually draw it. X * The following code will be executed if X * this is the case, and it will determine if X * a filled box is needed before rendering. X */ X if( RubberBox ) X { X /* set draw mode back to JAM1 since X * it is now currently set to COMPLEMENT X */ X SetDrMd( Window->RPort, JAM1 ); X RubberBox = FALSE; /* turn off rubberbanding */ X X /* Restore the condition of the RMBTRAP X * bit in the Window structure's Flags X * member. Menubutton events will no X * be received by this loop. X */ X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP ); X X /* RectFill is not condusive to the smooth X * execution of programs iit arguments are X * out of order, sot his code sorts them X * in preparation for the call. X */ X if( FilledBox ) X { X /* first sort the x-coords */ X if( TLX < OldBRX ) { X x1 = TLX; x2 = OldBRX; } X else { X x1 = OldBRX; x2 = TLX; } X X /* then sort the y-coords */ X if( TLY < OldBRY ) { X y1 = TLY; y2 = OldBRY; } X else { X y1 = OldBRY; y2 = TLY; } X X /* now generate the filled rectangle */ X RectFill( Window->RPort, (long)x1, (long)y1, X (long)x2, (long)y2 ); X } X else X { X /* FilledBox not set, so draw hollow box */ X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor ); X } X break; X } X X /* If execution comes here, then PenMode was X * not set and user was not rubberbanding. X * SELECTDOWN therefore indicates to start the X * rubberbanding process at this point. The X * initial coords are set to the values we X * received when the GetMsg() was executed. X */ X TLX = OldBRX = x; TLY = OldBRY = y; X X /* set to render in XOR mode */ X SetDrMd( Window->RPort, COMPLEMENT ); X X /* set flag to indicate we are now rubberbanding */ X RubberBox = TRUE; X X /* This instruction indicates to Intuition X * that we now wish to receive a message X * each time the Menubutton is pressed. X * This is how we hijack the right button X * for temporary use as a Cancel button X * instead of a Menubutton. X */ X Window->Flags |= RMBTRAP; X X /* render the initial rubberbox and exit */ X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor ); X break; X X case MENUDOWN: X /* WE only receive this message class if X * the RMBTRAP flag bit has been set, so X * it always means that we should cancel X * the box which is currently rubberbanding. X */ X /* turn the flag off */ X RubberBox = FALSE; X X /* restore control of menubutton to Intuition */ X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP ); X X /* erase (by double XOR'ing) the current X * rubberbox and exit switch. X */ X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor ); X break; X X default: X /* Something unimportant happened, so just X * continue thru the GetMsg() loop. X */ X continue; X } X break; X X case MENUPICK: X /* A menu event has taken place and is X * ready to be processed. Examine the X * code variable received from the message X * to determine what action should be taken. X * The first check is for MENUNULL, which X * means that nothing should be done at all. X */ X if( code != MENUNULL ) X { X /* get menu and item numbers from code */ X MenuNum = MENUNUM( code ); X ItemNum = ITEMNUM( code ); X X /* determine appropriate action by menu number */ X switch ( MenuNum ) X { X case 0: X /* Menu 0 is the Color menu. The X * item number indicates which new X * color to set. X */ X DrawColor = ItemNum; X SetAPen( Window->RPort, (long)DrawColor ); X break; X X case 1: X /* Menu 1 is the DrawMode menu. The item X * number indicates what to do. X * NOTE: Since we cannot have received X * this message if we were rubberbanding, X * then there is no need to clean up before X * changing drawing modes. X */ X switch ( ItemNum ) X { X case 0: X /* Erase window to current color */ X SetDrMd( Window->RPort, JAM1 ); X RectFill( Window->RPort, (long)MinX, (long)MinY, X (long)MaxX, (long)MaxY); X break; X X case 1: X /* set flag variables for hollow box */ X PenMode = FALSE; X FilledBox = FALSE; X break; X X case 2: X /* set flag variables for filled box */ X PenMode = FALSE; X FilledBox = TRUE; X break; X X case 3: X /* set flag variables for PenMode */ X PenMode = TRUE; X break; X X default: X /* don't do anything */ X break; X } X break; X X default: X /* Menu number unrecognized, do nothing */ X break; X } X } X break; X X case INACTIVEWINDOW: X /* User has de-selected our window, so a X * little bit of cleaning up may be needed X * to prevent untoward events when he comes X * back to it. X */ X /* erase any outstanding rubberbox */ X if( RubberBox ) X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor ); X X /* reset all the flafg variables */ X PenDown = ClippedLast = RubberBox = FALSE; X X /* return possibly diverted menubutton events to Big I */ X Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP ); X break; X X default: X /* message class was unrecognized, so do nothing */ X break; X } X } /* this brace ends the while(NewMessage) loop way back when */ X X /* There are no more messages waiting at the X * IDCMP port, so we can now proceed to X * process any MOUSEMOVED message we may X * have received. X */ X if( MouseMoved && !ClipIt) X { X /* the mouse did move, and we don't need to clip */ X X /* check first if we are drawing in PenMode */ X if( PenDown ) X { X /* We have to examine if we clipped the X * last PenMode draw operation. If we did, X * then this is the first move back into X * window boundaries, so we mov instead of X * drawing. X */ X if( ClippedLast ) X { X ClippedLast = FALSE; /* reset this flag now */ X Move( Window->RPort, (long)x, (long)y ); X } X else X Draw( Window->RPort, (long)x, (long)y ); /* draw to x,y coords */ X } X else X { X /* We weren't in PenMode, but we still might X * be rubberbanding a box. If so, then we X * should erase the current rubberbox and X * draw a new one with the new mouse coords. X */ X if( RubberBox ) X { X /* erase the old rubberbox - draw mode is COMPLEMENT */ X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor ); X X /* assign new values to box coords */ X OldBRX = x; OldBRY = y; X X /* and draw the new rubberbox */ X DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor ); X } X } X } X } X X /* It must be time to quit, so we have to clean X * up and exit. X */ X#ifdef TALKTOREXX X/* X * With Rexx, we need to bring the port down. You might make this X * part of exit() for programs that have multiple paths to exit. X */ X dnRexxPort() ; X#endif X ClearMenuStrip( Window ); X CloseWindow( Window ); X exit(TRUE); X } X#ifdef TALKTOREXX X/* X * Now we get into the actual code necessary for our REXX port; functions X * that do the real work. Note that this program was not structured X * particularly nicely for Rexx; I had to write each of these functions. X * Many programs have these subroutines already in place; they are called X * as part of the event loop. This progam, however, just has one big X * switch statement with different actions . . . X * X * First, our locals. X */ Xint currrexxcolor = 1 ; /* what color is *rexx* drawing in? */ Xint args[4] ; /* what args did we see to this function? */ Xint parsed ; /* was argument parsing successful? */ Xint userreplied ; /* has the current message been replied to yet? */ X/* X * This function takes a pointer to a pointer to a string, grabs the X * next number, returns it, and advances the pointer to the string to X * point after the number. X */ Xint getnm(where) Xchar **where ; X{ X register char *p = *where ; X register int val = 0 ; X int gotone = 0 ; X X while (*p <= ' ' && *p) X p++ ; X while ('0' <= *p && *p <= '9') { X gotone = 1 ; X val = 10 * val + *p++ - '0' ; X } X if (gotone == 0) X parsed = 0 ; X *where = p ; X return(val) ; X} X/* X * This function trys to find `n' numeric arguments in the command X * string, and stuffs them into the args array. X */ Xvoid parseargs(p, n) Xchar *p ; Xint n ; X{ X register int i ; X X while (*p > ' ' && *p) X p++ ; X for (i=0; i<n; i++) X args[i] = getnm(&p) ; X} X/* X * This is our main dispatch function. We check to make sure a Window X * currently exists. Then, we store away the `current color' and change X * it to Rexx's current color, call our handler function, and then restore X * the color. If our handler replied, we return a 1 to indicate that. X * If the parse and everything else was successful, we return a 0. X * Otherwise, we return a failure of 20 to indicate that the arguments X * were messed up. X */ Xint disp(msg, dat, p) Xregister struct RexxMsg *msg ; Xregister struct rexxCommandList *dat ; Xchar *p ; X{ X register int t ; X X parsed = 1 ; X if (Window) { X userreplied = 0 ; X t = Window->RPort->FgPen ; X SetAPen(Window->RPort, (long)currrexxcolor) ; X ((int (*)())(dat->userdata))(msg, p) ; X SetAPen(Window->RPort, (long)t) ; X if (! parsed) X replyRexxCmd(msg, (long)parsed, 0L, NULL) ; X return ; X } X replyRexxCmd(msg, 20L, 10L, NULL) ; X} X/* X * This handler sets the current rexx color. X */ Xvoid rexxcolor(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X parseargs(p, 1) ; X currrexxcolor = args[0] ; X} X/* X * This function silently clips the x and y values at `n' to the X * window bounds. X */ Xvoid clipxy(n) Xint n ; X{ X if (args[n] < MinX) X args[n] = MinX ; X if (args[n] > MaxX) X args[n] = MaxX ; X n++ ; X if (args[n] < MinY) X args[n] = MinY ; X if (args[n] > MaxY) X args[n] = MaxY ; X} X/* X * This handler grabs four arguments and draws a box. X */ Xvoid rexxbox(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X parseargs(p, 4) ; X clipxy(0) ; X clipxy(2) ; X DrawBox(args[0], args[1], args[2], args[3], Window, currrexxcolor) ; X} X/* X * This handler grabs four arguments and draws a filled box. X */ Xvoid rexxfbox(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X register int t ; X X parseargs(p, 4) ; X clipxy(0) ; X clipxy(2) ; X if (args[0] > args[2]) { X t = args[0] ; args[0] = args[2] ; args[2] = t ; X } X if (args[1] > args[3]) { X t = args[1] ; args[1] = args[3] ; args[3] = t ; X } X RectFill( Window->RPort, (long)args[0], (long)args[1], X (long)args[2], (long)args[3]) ; X} X/* X * This handler grabs four arguments and draws a line. X */ Xvoid rexxline(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X parseargs(p, 4) ; X clipxy(0) ; X clipxy(2) ; X Move(Window->RPort, (long)args[0], (long)args[1]) ; X Draw(Window->RPort, (long)args[2], (long)args[3]) ; X} X/* X * This handler pops the window to front. X */ Xvoid rexxtofront(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X WindowToFront(Window) ; X} X/* X * This handler pops the window to back. X */ Xvoid rexxtoback(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X WindowToBack(Window) ; X} X/* X * This handler sets the exit flag. X */ Xvoid rexxexit(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X KeepGoing = 0 ; X} X/* X * This handler returns the version of the program. X */ Xvoid rexxversion(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X userreplied = 1 ; X replyRexxCmd(msg, 0L, 0L, VERSION) ; X} X/* X * This handler sends the rest of the command asynchronously, X * allowing us to run macros in parallel. X */ Xvoid rexxspawn(msg, p) Xstruct RexxMsg *msg ; Xchar *p ; X{ X while (*p <= ' ' && *p) X p++ ; X asyncRexxCmd(p) ; X} X#endif SHAR_EOF echo "extracting minrexx.c" sed 's/^X//' << \SHAR_EOF > minrexx.c X/* X * This is an example of how REXX messages might be handled. This is X * a `minimum' example that both accepts asynchronous REXX messages and X * can request REXX service. X * X * Read this entire file! It's short enough. X * X * It is written in such a fashion that it can be attached to a program X * with a minimum of fuss. The only external symbols it makes available X * are the seven functions and RexxSysBase. X * X * This code is by Radical Eye Software, but it is put in the public X * domain. I would appreciate it if the following string was left in X * both as a version check and as thanks from you for the use of this X * code. X * X * If you modify this file for your own use, don't bump the version X * number; add a suffix, such as 1.0a or 1.0.3 or something, so we X * don't have fake `versions' floating around. X */ Xstatic char *blurb = "Radical Eye MinRexx 0.4" ; X/* X * We read in our own personal little include. X */ X#include "minrexx.h" X/* X * All of our local globals, hidden from sight. X */ Xstatic struct MsgPort *rexxPort ; /* this is *our* rexx port */ Xstatic int bringerdown ; /* are we trying to shut down? */ Xstatic struct rexxCommandList *globalrcl ; /* our command association list */ Xstatic long stillNeedReplies ; /* how many replies are pending? */ Xstatic long rexxPortBit ; /* what bit to wait on for Rexx? */ Xstatic char *extension ; /* the extension for macros */ Xstatic int (*userdisp)() ; /* the user's dispatch function */ Xstatic struct RexxMsg *oRexxMsg ; /* the outstanding Rexx message */ X/* X * Our library base. Don't you dare close this! X */ Xstruct RxsLib *RexxSysBase ; X/* X * This is the main entry point into this code. X */ Xlong upRexxPort(s, rcl, exten, uf) X/* X * The first argument is the name of your port to be registered; X * this will be used, for instance, with the `address' command of ARexx. X */ Xchar *s ; X/* X * The second argument is an association list of command-name/user-data X * pairs. It's an array of struct rexxCommandList, terminated by a X * structure with a NULL in the name field. The commands are case X * sensitive. The user-data field can contain anything appropriate, X * perhaps a function to call or some other data. X */ Xstruct rexxCommandList *rcl ; X/* X * The third argument is the file extension for ARexx macros invoked X * by this program. If you supply this argument, any `primitive' not X * in the association list rcl will be sent out to ARexx for X * interpretation, thus allowing macro programs to work just like X * primitives. If you do not want this behavior, supply a `NULL' X * here, and those commands not understood will be replied with an X * error value of RXERRORNOCMD. X */ Xchar *exten ; X/* X * The fourth argument is the user dispatch function. This function X * will *only* be called from rexxDisp(), either from the user calling X * this function directly, or from dnRexxPort(). Anytime a command X * match is found in the association list, this user-supplied function X * will be called with two arguments---the Rexx message that was X * received, and a pointer to the association pair. This function X * should return a `1' if the message was replied to by the function X * and a `0' if the default success code of (0, 0) should be returned. X * Note that the user function should never ReplyMsg() the message; X * instead he should indicate the return values with replyRexxCmd(); X * otherwise we lose track of the messages that still lack replies. X */ Xint (*uf)() ; X/* X * upRexxPort() returns the signal bit to wait on for Rexx messages. X * If something goes wrong, it simply returns a `0'. Note that this X * function is safe to call multiple times because we check to make X * sure we haven't opened already. It's also a quick way to change X * the association list or dispatch function. X */ X{ X struct MsgPort *FindPort() ; X struct MsgPort *CreatePort() ; X X/* X * Some basic error checking. X */ X if (rcl == NULL || uf == NULL) X return(0L) ; X/* X * If we aren't open, we make sure no one else has opened a port with X * this name already. If that works, and the createport succeeds, we X * fill rexxPortBit with the value to return. X * X * Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check X * for rexxPort == NULL also insures that our rexxPortBit is 0. X */ X if (rexxPort == NULL) { X Forbid() ; X if (FindPort(s)==NULL) X rexxPort = CreatePort(s, 0L) ; X Permit() ; X if (rexxPort != NULL) X rexxPortBit = 1L << rexxPort->mp_SigBit ; X } X/* X * Squirrel away these values for our own internal access, and return X * the wait bit. X */ X globalrcl = rcl ; X extension = exten ; X userdisp = uf ; X return(rexxPortBit) ; X} X/* X * This function closes the rexx library, but only if it is open X * and we aren't expecting further replies from REXX. It's X * *private*, but it doesn't have to be; it's pretty safe to X * call anytime. X */ Xstatic void closeRexxLib() { X if (stillNeedReplies == 0 && RexxSysBase) { X CloseLibrary(RexxSysBase) ; X RexxSysBase = NULL ; X } X} X/* X * This function closes down the Rexx port. It is always safe to X * call, and should *definitely* be made a part of your cleanup X * routine. No arguments and no return. It removes the Rexx port, X * replies to all of the messages and insures that we get replies X * to all the ones we sent out, closes the Rexx library, deletes the X * port, clears a few flags, and leaves. X */ Xvoid dnRexxPort() { X if (rexxPort) { X RemPort(rexxPort) ; X bringerdown = 1 ; X/* X * A message still hanging around? We kill it off. X */ X if (oRexxMsg) { X oRexxMsg->rm_Result1 = RXERRORIMGONE ; X ReplyMsg(oRexxMsg) ; X oRexxMsg = NULL ; X } X while (stillNeedReplies) { X WaitPort(rexxPort) ; X dispRexxPort() ; X } X closeRexxLib() ; X DeletePort(rexxPort) ; X rexxPort = NULL ; X } X rexxPortBit = 0 ; X} X/* X * Here we dispatch any REXX messages that might be outstanding. X * This is the main routine for handling Rexx messages. X * This function is fast if no messages are outstanding, so it's X * pretty safe to call fairly often. X * X * If we are bring the system down and flushing messages, we reply X * with a pretty serious return code RXERRORIMGONE. X * X * No arguments, no returns. X */ Xvoid dispRexxPort() { X register struct RexxMsg *GetMsg() ; X register struct RexxMsg *RexxMsg ; X int cmdcmp() ; X register struct rexxCommandList *rcl ; X register char *p ; X register int dontreply ; X X/* X * If there's no rexx port, we're out of here. X */ X if (rexxPort == NULL) X return ; X/* X * Otherwise we have our normal loop on messages. X */ X while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) { X/* X * If we have a reply to a message we sent, we look at the second X * argument. If it's set, it's a function we are supposed to call X * so we call it. Then, we kill the argstring and the message X * itself, decrement the outstanding count, and attempt to close X * down the Rexx library. Note that this call only succeeds if X * there are no outstanding messages. Also, it's pretty quick, so X * don't talk to me about efficiency. X */ X if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) { X if (RexxMsg->rm_Args[1]) { X ((int (*)())(RexxMsg->rm_Args[1]))(RexxMsg) ; X } X DeleteArgstring(RexxMsg->rm_Args[0]) ; X DeleteRexxMsg(RexxMsg) ; X stillNeedReplies-- ; X closeRexxLib() ; X/* X * The default case is we got a message and we need to check it for X * primitives. We skip past any initial tabs or spaces and initialize X * the return code fields. X */ X } else { X p = (char *)RexxMsg->rm_Args[0] ; X while (*p > 0 && *p <= ' ') X p++ ; X RexxMsg->rm_Result1 = 0 ; X RexxMsg->rm_Result2 = 0 ; X/* X * If somehow the reply is already done or postponed, `dontreply' is X * set. X */ X dontreply = 0 ; X/* X * If the sky is falling, we just blow up and replymsg. X */ X if (bringerdown) { X RexxMsg->rm_Result1 = RXERRORIMGONE ; X/* X * Otherwise we cdr down our association list, comparing commands, X * until we get a match. If we get a match, we call the dispatch X * function with the appropriate arguments, and break out. X */ X } else { X oRexxMsg = RexxMsg ; X for (rcl = globalrcl; rcl->name; rcl++) { X if (cmdcmp(rcl->name, p) == 0) { X userdisp(RexxMsg, rcl, p+strlen(rcl->name)) ; X break ; X } X } X/* X * If we broke out, rcl will point to the command we executed; if we X * are at the end of the list, we didn't understand the command. In X * this case, if we were supplied an extension in upRexxPort, we know X * that we should send the command out, so we do so, synchronously. X * The synchronous send takes care of our reply. If we were given a X * NULL extension, we bitch that the command didn't make sense to us. X */ X if (rcl->name == NULL) { X if (extension) { X syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ; X dontreply = 1 ; X } else { X RexxMsg->rm_Result1 = RXERRORNOCMD ; X } X } X } X/* X * Finally, reply if appropriate. X */ X oRexxMsg = NULL ; X if (! dontreply) X ReplyMsg(RexxMsg) ; X } X } X} X/* X * This is the function we use to see if the command matches X * the command string. Not case sensitive, and the real command only X * need be a prefix of the command string. Make sure all commands X * are given in lower case! X */ Xstatic int cmdcmp(c, m) Xregister char *c, *m ; X{ X while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) { X c++ ; X m++ ; X } X return(*c) ; X} X/* X * Opens the Rexx library if unopened. Returns success (1) or X * failure (0). This is another function that is *private* but X * that doesn't have to be. X */ Xstatic int openRexxLib() { X struct RxsLib *OpenLibrary() ; X X if (RexxSysBase) X return(1) ; X return((RexxSysBase = OpenLibrary(RXSNAME, 0L)) != NULL) ; X} X/* X * This is the general ARexx command interface, but is not the one X * you will use most of the time; ones defined later are easier to X * understand and use. But they all go through here. X */ Xstruct RexxMsg *sendRexxCmd(s, f, p1, p2, p3) Xchar *s ; X/* X * The first parameter is the command to send to Rexx. X */ Xint (*f)() ; X/* X * The second parameter is either NULL, indicating that the command X * should execute asynchronously, or a function to be called when the X * message we build up and send out here finally returns. Please note X * that the function supplied here could be called during cleanup after X * a fatal error, so make sure it is `safe'. This function always is X * passed one argument, the RexxMsg that is being replied. X */ XSTRPTR p1, p2, p3 ; X/* X * These are up to three arguments to be stuffed into the RexxMsg we X * are building up, making the values available when the message is X * finally replied to. The values are stuffed into Args[2]..Args[4]. X */ X{ X struct RexxMsg *CreateRexxMsg() ; X STRPTR CreateArgstring() ; X register struct MsgPort *rexxport ; X register struct RexxMsg *RexxMsg ; X X/* X * If we have too many replies out there, we just return failure. X * Note that you should check the return code to make sure your X * message got out! Then, we forbid, and make sure that: X * - we have a rexx port open X * - Rexx is out there X * - the library is open X * - we can create a message X * - we can create an argstring X * X * If all of these succeed, we stuff a few values and send the X * message, permit, and return. X */ X if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1) X return(NULL) ; X RexxMsg = NULL ; X if (openRexxLib() && (RexxMsg = X CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) && X (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) { X RexxMsg->rm_Action = RXCOMM ; X RexxMsg->rm_Args[1] = (STRPTR)f ; X RexxMsg->rm_Args[2] = p1 ; X RexxMsg->rm_Args[3] = p2 ; X RexxMsg->rm_Args[4] = p3 ; X RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ; X Forbid() ; X if (rexxport = FindPort(RXSDIR)) X PutMsg(rexxport, RexxMsg) ; X Permit() ; X if (rexxport) { X stillNeedReplies++ ; X return(RexxMsg) ; X } else X DeleteArgstring(RexxMsg->rm_Args[0]) ; X } X if (RexxMsg) X DeleteRexxMsg(RexxMsg) ; X closeRexxLib() ; X return(NULL) ; X} X/* X * This function is used to send out an ARexx message and return X * immediately. Its single parameter is the command to send. X */ Xstruct RexxMsg *asyncRexxCmd(s) Xchar *s ; X{ X return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ; X} X/* X * This function sets things up to reply to the message that caused X * it when we get a reply to the message we are sending out here. X * But first the function we pass in, which actually handles the reply. X * Note how we get the message from the Args[2]; Args[0] is the command, X * Args[1] is this function, and Args[2]..Args[4] are any parameters X * passed to sendRexxCmd() as p1..p3. We pass the result codes right X * along. X */ Xstatic void replytoit(msg) Xregister struct RexxMsg *msg ; X{ X register struct RexxMsg *omsg ; X X omsg = (struct RexxMsg *)(msg->rm_Args[2]) ; X replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ; X ReplyMsg(omsg) ; X} X/* X * This function makes use of everything we've put together so far, X * and functions as a synchronous Rexx call; as soon as the macro X * invoked here returns, we reply to `msg', passing the return codes X * back. X */ Xstruct RexxMsg *syncRexxCmd(s, msg) Xchar *s ; Xstruct RexxMsg *msg ; X{ X return(sendRexxCmd(s, (APTR)&replytoit, msg, NULL, NULL)) ; X} X/* X * There are times when you want to pass back return codes or a X * return string; call this function when you want to do that, X * and return `1' from the user dispatch function so the main X * event loop doesn't reply (because we reply here.) This function X * always returns 1. X */ Xvoid replyRexxCmd(msg, primary, secondary, string) X/* X * The first parameter is the message we are replying to. X */ Xregister struct RexxMsg *msg ; X/* X * The next two parameters are the primary and secondary return X * codes. X */ Xregister long primary, secondary ; X/* X * The final parameter is a return string. This string is only X * returned if the primary return code is 0, and a string was X * requested. X * X * We also note that we have replied to the message that came in. X */ Xregister char *string ; X{ X STRPTR CreateArgstring() ; X X/* X * Note how we make sure the Rexx Library is open before calling X * CreateArgstring . . . and we close it down at the end, if possible. X */ X if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) { X if (string && openRexxLib()) X secondary = (long)CreateArgstring(string, (long)strlen(string)) ; X else X secondary = 0L ; X } X msg->rm_Result1 = primary ; X msg->rm_Result2 = secondary ; X closeRexxLib() ; X} SHAR_EOF echo "extracting minrexx.h" sed 's/^X//' << \SHAR_EOF > minrexx.h X/* X * Includes for minrexx.c; please refer to that file for X * further documentation. X */ X#include <rexx/rxslib.h> X/* X * This is the list of functions we can access. (Cheap forward X * declarations, too.) X */ Xlong upRexxPort() ; Xvoid dnRexxPort() ; Xvoid dispRexxPort() ; Xstruct RexxMsg *sendRexxCmd() ; Xstruct RexxMsg *syncRexxCmd() ; Xstruct RexxMsg *asyncRexxCmd() ; Xvoid replyRexxCmd() ; X/* X * Maximum messages that can be pending, and the return codes X * for two bad situations. X */ X#define MAXRXOUTSTANDING (300) X#define RXERRORIMGONE (100) X#define RXERRORNOCMD (30) X/* X * This is the association list you build up (statically or X * dynamically) that should be terminated with an entry with X * NULL for the name . . . X */ Xstruct rexxCommandList { X char *name ; X APTR userdata ; X} ; SHAR_EOF echo "extracting Makefile" sed 's/^X//' << \SHAR_EOF > Makefile Xall: freedraw origfreedraw X Xfreedraw: freedraw.o minrexx.o rexxglue.o X ln freedraw.o minrexx.o rexxglue.o -lc X Xfreedraw.o: freedraw.c minrexx.h X cc -DTALKTOREXX freedraw.c X Xminrexx.o: minrexx.c minrexx.h X cc minrexx.c X Xorigfreedraw: origfreedraw.o X ln origfreedraw.o -lc X Xorigfreedraw.o: freedraw.c X cc -o origfreedraw.o freedraw.c X Xclean: X delete freedraw.o origfreedraw.o freedraw origfreedraw minrexx.o SHAR_EOF echo "extracting freeedraw.uu" sed 's/^X//' << \SHAR_EOF > freeedraw.uu X Xbegin 700 freedraw XM```#\P`````````#``````````(```?Q````QP````$```/I```'\4[Z%L9&) XM<F5E9')A=R`P+C`Q(&)Y(%)I8VAA<F0@32X@4F]S<P``04U)1T$@1G)E941RZ XM87<@,"XP,0!C;VQO<@!B;W@`9F)O>`!L:6YE`'1O9G)O;G0`=&]B86-K`&5X3 XM:70`=F5R<VEO;@!S<&%W;@!.5?_^(&T`$")H`#(;:0`9__\0+0`52(!(P"\`Q XM(&T`$"\H`#).NAZ>4$\P+0`*2,`O`#`M``A(P"\`(&T`$"\H`#).NAY<3^\`I XM##`M``I(P"\`,"T`#$C`+P`@;0`0+R@`,DZZ'BI/[P`,,"T`#DC`+P`P+0`,5 XM2,`O`"!M`!`O*``R3KH>"D_O``PP+0`.2,`O`#`M``A(P"\`(&T`$"\H`#).) XMNAWJ3^\`##`M``I(P"\`,"T`"$C`+P`@;0`0+R@`,DZZ'<I/[P`,$"W__TB`G XM2,`O`"!M`!`O*``R3KH=YE!/3EU.=4Y5__PP+0`(4T!(P..`0>R`ACMP"`#_; XM_$)M__Y@``&6,"W__L'\`")![(#(,BW__E)!P_P`(D/L@,C2B2&!"``P+?_^S XMP?P`(D'L@-HR+?_^P_P`%$/L@5#2B2&!"``P+?_^P?P`(D'L@,PR+?_^2,&#T XM_``$2$'#_``H5$$Q@0@`,"W__L'\`")![(#.,BW__DC!@_P`!,/\`!DQ@0@`M XM,"W__L'\`")![(#0,;P`*`@`,"W__L'\`")![(#2,;P`&0@`,"W__L'\`")!3 XM[(#4,;P`D`@`,"W__L'\`")![(#60K`(`#`M__[!_``B0>R`WD*P"``P+?_^3 XMP?P`(D'L@.)",`@`,"W__L'\`")![(#D0K`(`#`M__[!_``B0>R`Z$)P"``PD XM+?_^P?P`%$'L@5`QO``!"``P+?_^P?P`%$'L@5(QO``!"``P+?_^P?P`%$'LH XM@50QO``F"``P+?_^P?P`%$'L@58QO``7"``P+?_^P?P`%$'L@5@QK0`("``PZ XM+?_^P?P`%$'L@5I"L`@`,"W__L'\`!1![(%>0C`(`#`M__[!_``40>R!7Q&MD XM__\(`%)M__XP+?_^L&W__&T`_F(P+?_\4T#!_``B0>R`R$*P"`!P`$Y=3G5.F XM5?_^0FW__C`M__[!_``B0>R!H#(M__Y20</\`")#[(&@THDA@0@`,"W__L'\! XM`")![(&D0G`(`#`M__[!_``B0>R!IC(M__[#_``*,8$(`#`M__[!_``B0>R!! XMJ#&\`'`(`#`M__[!_``B0>R!JC&\``H(`#`M__[!_``B0>R!K#&\`)((`#`M4 XM__[!_``B0>R!KD*P"``P+?_^P?P`(D'L@;(R+?_^P_P`%$/L@BC2B2&!"``P( XM+?_^P?P`(D'L@;9"L`@`,"W__L'\`")![(&Z0C`(`#`M__[!_``B0>R!O$*P7 XM"``P+?_^P?P`(D'L@<!"<`@`,"W__L'\`!1![((H0C`(`#`M__[!_``40>R"_ XM*1&\``$(`#`M__[!_``40>R"*A&\``$(`#`M__[!_``40>R"+$)P"``P+?_^S XMP?P`%$'L@BXQO``!"``P+?_^P?P`%$'L@C!"L`@`,"W__L'\`!1![((X0K`(T XM`%)M__X,;0`$__YM`/Z20JR"!D'Z`"0I2((T0?H`)BE(@DA!^@`I*4B"7$'ZY XM`"PI2()P<`!.74YU17)A<V4@06QL`$AO;&QO=R!";W@`1FEL;&5D($)O>`!0] XM96X@1')A=P``3E4``$'L@I8I2()X.7P`"H)\0FR"?CE\`#*"@#E\``J"@CE\@ XM``&"A$'Z`$0I2(*&0>R`R"E(@HI"K(*6.7P`08*:0FR"G#E\`%6"GCE\``J"` XMH#E\``&"HD'Z`!HI2(*D0>R!H"E(@JAP`$Y=3G5#;VQO<@!$<F%W36]D90``_ XM3E7^RAM\``'_^SM\`![_^#M\`![_]CM\`!3_]#M\`!3_\D)M_]I";?_80FW_) XMUD)M_]1";?_20FW_T$)M_\Y(>``!2'H&JDZZ&-Q03RE`@,!*K(#`9@A"ITZZ> XM%7!83TAX``%(>@:<3KH8O%!/*4"`Q$JL@,1F"$*G3KH54%A/2&R`!DZZ&998G XM3RE`@K1F"$*G3KH5.%A/(&R"M!`H`#9(@#E`@K@@;(*T$"@`-TB`.4""NB!LF XM@K0B;(*T$"D`.$B`,B@`"))`4T$Y08*\(&R"M")L@K00*0`Y2(`R*``*DD!3\ XM03E!@KX_/``"3KKZ\E1/3KK\Q$ZZ_HQ(;()X+RR"M$ZZ&2103T*G(&R"M"\H. XM`#).NABZ4$\0+?_[2(`_`"!L@K0O*``R3KH8E%Q/2'H&H$AZ!>A(;(`V2'H%; XMUTZZ"<I/[P`0*T#_RD(M_LH[?``!_^I@+#`M_^I(P.6`(&T`"B\P"`!(;?[*& XM3KH38%!/2'H%K4AM_LI.NA-24$]2;?_J,"W_ZK!M``AMRDHM_LIG"DAM_LI.< XMN@S@6$]*;("09P`%*"!L@K0B:`!6<``0*0`/<@'AH8*M_\HO`4ZZ%ZY83TZZ: XM"D!";?_:(&R"M"\H`%9.NA<P6$\K0/_\9P`$*B!M__PK:``4_^X@;?_\.V@`Y XM&/_L(&R"M#MH``[_ZB!L@K0[:``,_^@O+?_\3KH73EA/,"W_ZK!L@KAM'C`MF XM_^JP;(*\;A0P+?_HL&R"NFT*,"W_Z+!L@KYO"#M\``'_V&`$0FW_V&<,2FW_1 XMTF<&.WP``?_6("W_[F```WH[?``!_]I@``.@(&R"M!`H`#9(@#E`@K@@;(*TD XM$"@`-TB`.4""NB!L@K0B;(*T$"D`.$B`,B@`"))`4T$Y08*\(&R"M")L@K00! XM*0`Y2(`R*``*DD!303E!@KY@``-,0FR`D&```T1P`#`M_^Q@``'`0FW_UD)M. XM_])@``'.2FW_V&8``<9*;?_49SX[?``!_])";?_60J<@;(*T+R@`,DZZ%M)0K XM3S`M_^A(P"\`,"W_ZDC`+P`@;(*T+R@`,DZZ%H!/[P`,8``!@DIM_]!G``#(2 XM0J<@;(*T+R@`,DZZ%I903T)M_]`@;(*T"*@````92FW_SF=Z,"W_]+!M__AL% XM#CMM__3_YCMM__C_XF`,.VW_^/_F.VW_]/_B,"W_\K!M__9L#CMM__+_Y#MMX XM__;_X&`,.VW_]O_D.VW_\O_@,"W_X$C`+P`P+?_B2,`O`#`M_^1(P"\`,"W_S XMYDC`+P`@;(*T+R@`,DZZ%>I/[P`48"00+?_[2(`_`"\L@K0_+?_V/RW_^#\M$ XM__(_+?_T3KKW$D_O``Y@``"T.VW_ZO_X.VW_ZO_T.VW_Z/_V.VW_Z/_R2'@`1 XM`B!L@K0O*``R3KH5ME!/.WP``?_0(&R"M`CH````&1`M__M(@#\`+RR"M#\M, XM__8_+?_X/RW_\C\M__1.NO:P3^\`#F!20FW_T"!L@K0(J````!D0+?_[2(`_' XM`"\L@K0_+?_V/RW_^#\M__(_+?_T3KKV?$_O``Y@'F``_4*0O````&AG`/Y&3 XM4X!GNI"\````?V<`_BQ@XF```5X,;?___^QG``#>,"W_[,!\`!\[0/_>,"W_/ XM[.I(P'P`/SM`_]QP`#`M_]Y@``"L&VW_W?_[$"W_^TB`2,`O`"!L@K0O*``R$ XM3KH4SE!/8```F'``,"W_W&!J0J<@;(*T+R@`,DZZ%,)03S`L@KY(P"\`,"R"5 XMO$C`+P`P+(*Z2,`O`#`L@KA(P"\`(&R"M"\H`#).NA1R3^\`%&`Z0FW_U$)M% XM_\Y@,$)M_]0[?``!_\Y@)#M\``'_U&`<8!K_AO_(_]+_WK"\````!&3NXX`PX XM.P#L3OL``&`08`Y*@&<`_U)3@&<`_VY@\&!T2FW_T&<D$"W_^TB`/P`O+(*T[ XM/RW_]C\M__@_+?_R/RW_]$ZZ]49/[P`.0FW_T$)M_]9";?_2(&R"M`BH````# XM&6`R8#!5@&<`_(Y=@&<`_.11@&<`_'B0O````/!G`/ZZD+P```$`9P#\PI"\J XM``?^`&>.8,Y@`/O$2FW_VF<``+A*;?_89@``L$IM_])G3DIM_]9G)D)M_]8PD XM+?_H2,`O`#`M_^I(P"\`(&R"M"\H`#).NA-83^\`#&`@,"W_Z$C`+P`P+?_J1 XM2,`O`"!L@K0O*``R3KH3)$_O``Q@6DIM_]!G5!`M__M(@#\`+RR"M#\M__8_U XM+?_X/RW_\C\M__1.NO1J3^\`#CMM_^K_^#MM_^C_]A`M__M(@#\`+RR"M#\M9 XM__8_+?_X/RW_\C\M__1.NO0Z3^\`#F``^M1.N@32+RR"M$ZZ$S!83R\L@K1.$ XMNA,R6$](>``!3KH.Y%A/3EU.=6EN='5I=&EO;BYL:6)R87)Y`&=R87!H:6-S- XM+FQI8G)A<GD`9G)E961R87<`9F0`(```3E7__DCG""`@;0`()%!X`$)M__X,- XM$@`@;@A*$F<$4HI@\G`PL!)N)`P2`#EN'CM\``'__B!*4HH0$$B`,@3#_``*6 XMT$$X`)A\`#!@UDIM__YF!$)L@L@@;0`(((HP!$S?!!!.74YU3E4``"\$(&T`< XM"`P0`"!O#B!M``A*$&<&4JT`"&#H>`!@&DAM``A.NO]R6$\R!$C!XX%![(+`- XM,8`8`%)$N&T`#&W@*!].74YU3E4``$CG"#`D;0`()FT`##E\``&"R$JL@K1GG XM<$)L@LH@;(*T(F@`,A`I`!E(@#@`,"R`DDC`+P`@;(*T+R@`,DZZ$:Q03R\MJ XM`!`O"B!K``1.D%!/,`1(P"\`(&R"M"\H`#).NA&*4$]*;(+(9A9"IT*G,"R"Y XMR$C`+P`O"DZZ!IA/[P`03-\,$$Y=3G5"ITAX``I(>``4+PI.N@9\3^\`$&#B[ XM3E4``#\\``$O+0`,3KK_"%Q/.6R"P("23EU.=4Y5```P+0`(2,#C@$'L@L`R\ XM,`@`LFR"N&P2,"T`"$C`XX!![(+`,:R"N`@`,"T`"$C`XX!![(+`,C`(`+)LS XM@KQO$C`M``A(P..`0>R"P#&L@KP(`%)M``@P+0`(2,#C@$'L@L`R,`@`LFR"Q XMNFP2,"T`"$C`XX!![(+`,:R"N@@`,"T`"$C`XX!![(+`,C`(`+)L@KYO$C`M] XM``A(P..`0>R"P#&L@KX(`$Y=3G5.50``/SP`!"\M``Q.NOY`7$]"9TZZ_SY4R XM3S\\``).NO\T5$\_+("2+RR"M#\L@L8_+(+$/RR"PC\L@L!.NO&"3^\`#DY=J XM3G5.50``+P0_/``$+RT`#$ZZ_?9<3T)G3KK^]%1//SP``DZZ_NI43S`L@L"P* XM;(+$;PXX+(+`.6R"Q(+`.42"Q#`L@L*P;(+&;PXX+(+".6R"QH+".42"QC`L` XM@L9(P"\`,"R"Q$C`+P`P+(+"2,`O`#`L@L!(P"\`(&R"M"\H`#).N@^J3^\`. XM%"@?3EU.=4Y5```_/``$+RT`#$ZZ_6Q<3T)G3KK^:E1//SP``DZZ_F!43S`L- XM@L)(P"\`,"R"P$C`+P`@;(*T+R@`,DZZ#TY/[P`,,"R"QDC`+P`P+(+$2,`O^ XM`"!L@K0O*``R3KH/'$_O``Q.74YU3E4``"\L@K1.N@_"6$].74YU3E4``"\L6 XM@K1.N@^D6$].74YU3E4``$)L@)!.74YU3E4``#E\``&"RB\L@`)"IT*G+RT`% XM"$ZZ!"A/[P`03EU.=4Y5```@;0`,#!``(&X.(&T`#$H09P92K0`,8.@O+0`,' XM3KH#CEA/3EU.=5)A9&EC86P@17EE($UI;E)E>'@@,"XT`$Y5``!*K0`,9P9*; XMK0`49@9P`$Y=3G5*K("B9CY.N@VP+RT`"$ZZ#8Q83TJ`9A!"IR\M``A.N@R`9 XM4$\I0("B3KH-\$JL@*)G$B!L@*)P`!`H``]R`>&A*4&`L"EM``R`J"EM`!"`D XMM"EM`!2`N"`L@+!@H$Y5``!*K("L9A1*K(+,9PXO+(+,3KH,'EA/0JR"S$Y=S XM3G5.50``2JR`HF=4+RR`HDZZ#:)83SE\``&`IDJL@+QG&B!L@+PA?````&0`L XM("\L@+Q.N@V,6$]"K("\2JR`K&<.+RR`HDZZ#9183V$:8.QACB\L@*).N@Q8L XM6$]"K("B0JR`L$Y=3G5.50``2.<,,$JL@*)F"$S?##!.74YU+RR`HDZZ#.18Y XM3R1`2H!G``#8#"H`!P`(9BY*J@`L9PHO"B!J`"Q.D%A/+RH`*$ZZ`R)83R\*H XM3KH#(%A/4ZR`K$ZZ_R!@``">*"H`*"!$2A!O#"!$#!``(&X$4H1@[D*J`"!"9 XMJ@`D>@!*;("F9PHE?````&0`(&!<*4J`O"9L@*A@+"\$+Q-A9%!/2D!F'B\34 XM3KH$E%A/2,#0A"\`+PLO"B!L@+A.D$_O``Q@!E"+2I-FT$J39AY*K("T9Q`OV XM"B\J`"A.N@'H4$]Z`6`()7P````>`"!"K("\2D5F""\*3KH,8EA/8`#_&F``S XM_PY.50``2.<`,"1M``@F;0`,2A)G*!`2L!-G'!`32(#0?``@$A)(@;!!9A)PW XM8;`2;@P,$@!Z;@92BE*+8-00$DB`3-\,`$Y=3G5.50``2JR"S&<&<`%.74YU8 XM0J=(>@`63KH+P%!/*4""S&<$<`%@`G``8.)R97AX<WES;&EB+FQI8G)A<GD`: XM`$Y5``!(YP`P2JR`HF<*#*P```$K@*QO"G``3-\,`$Y=3G67RV&<2D!G``"B; XM(&R`HB\H``HO+("T+RR`HDZZ`9Q/[P`,)D!*@&<``((O+0`(3KH#8EA/2,`O2 XM`"\M``A.N@)(4$\G0``H9V(G?`$`````'"=M``P`+"=M`!``,"=M`!0`-"=M] XM`!@`.$'Z`%8G2``*3KH*N$AZ`$].N@J46$\D0$J`9PHO"R\*3KH+"E!/3KH*B XM_"`*9PI2K("L(`M@`/]:+RL`*$ZZ`1A83R`+9P@O"TZZ`1)83TZZ_19P`&``P XM_SI215A8`%)%6%@`3E4``$*G0J="IT*G+RT`"$ZZ_P)/[P`43EU.=4Y5``!(F XMYP`P)&T`""9J`#!"IR\J`"0O*@`@+PMA-$_O`!`O"TZZ"J)83TS?#`!.74YUF XM3E4``$*G0J<O+0`,2'K_P"\M``A.NOZP3^\`%$Y=3G5.50``2.<,,"1M``@H' XM+0`,*BT`$"9M`!1*A&8N""H``0`=9R8@"V<@3KK^/DI`9Q@O"TZZ`B983TC`> XM+P`O"TZZ`0Y03RH`8`)Z`"5$`"`E10`D3KK\0DS?##!.74YU,CS_4F!P,CS^2 XMJF!J,CS_$&!D<I1@8#(\_S1@6C(\_FA@5#(\_W!@3C(\_WQ@2#(\_VI@0C(\A XM_GI@/#(\_G1@-C(\_FY@,#(\_F)@*C(\_H!@)#(\_TQ@'G*(8!HR//\68!1R0 XMFF`0,CS_(F`*,CS_'&`$,CS_6$SO`P``!"`O``PO#BQY```"RDZV$``L7TYUB XM,CS_=F#@,CS_.F#:,CS^C,CS^GF#.,CS^I&#(,CS_1F#",CS_0&"\,CS^J XM1&"V,CS_!&"P,CS^_F"J,CS^^&"D,CS^\F">,CS^[&"8,CS^X&"2,CS^AF",_ XM,CS^F&"&<H)@(#(\_RA@&C(\_V1@%#(\_E!@#G*.8`HR//\N8`0R//[F(&\`F XM!"`O``A@`/]B,CS^R&`0,CS^/F`*,CS^VF`$,CS^."`O``1@`/]$,GS^SF`0" XM,GS^PF`*,GS_7F`$,GS^DB!O``1,[P`#``@O#BQY```"RDZVD``L7TYU3.\#L XM`P`$P8C#B2\.+'D```+*3J[^2BQ?3G5,[P,#``3!B,.)+PXL>0```LI.KOZ\: XM+%].=4SO`P,`!,&(PXDO#BQY```"RDZN_K8L7TYU3.\#`P`$P8C#B2\.+'D`0 XM``+*3J[^L"Q?3G4@;P`$,CS^U&$`_J`B;P`((H%.=2`O``1RH&$`_HXB;P`(` XM(HA.=2!O``0@"$H89OR1P"`(4X!.=6%P0^R`HD7L@**UR68.,CP`GFL(=``BW XMPE')__PI3X+0+'@`!"E.@M1(YX"`""X`!`$I9Q!+^@`(3J[_XF`&0J?S7TYSL XM0_H`($ZN_F@I0(+89@PN/``#@`=.KO^48`1.N@`:4$].=61O<RYL:6)R87)Y( XM`$GY``!__DYU3E4``"\*2'D``0``,"R`F,'\``8O`$ZZ!LHI0(+<4$]F%$*GG XM2'D``0``3KH%H%!/+FR"T$YU(&R"W$)H``0@;(+<,7P``0`0(&R"W#%\``$`5 XM"B!L@M`@+(+0D*@`!%"`*4""X"!L@N`@O$U!3EA"ITZZ!HXD0$JJ`*Q83V<N@ XM+RT`#"\M``@O"DZZ`*XY?``!@N0@;(+<`&B````$(&R"W`!H@```"D_O``Q@S XM0DAJ`%Q.N@;^2&H`7$ZZ!H(I0(+F(&R"YDJH`"103V<0(&R"YB)H`"0O$4ZZ5 XM!+I83R\L@N8O"DZZ`F@I;(+F@NI03TZZ!*X@;(+<((!.N@2Z(&R"W"%```9G= XM%DAX`^U(>@`J3KH$EB!L@MPA0``,4$\O+(+J/RR"[DZZ[1!"9TZZ`M103R1?: XM3EU.=2H`3E4``$CG##`D;0`0(&T`"$JH`*QG&"!M``@@*`"LY8`H`"!$("@`[ XM$.6`)D!@!"9L@)H0$TB`2,#0K0`,5(`Y0(+P0J<P+(+P2,`O`$ZZ!5PI0(+R: XM4$]F"$S?##!.74YU$!-(@#H`/P4@2U*(+P@O+(+R3KH!?C`%2,`@0-'L@O)#N XM^@%$$-EF_#\M``XO"B\L@O).N@$Z(&R"\D(P4``Y?``!@NXP!4C`T*R"\B9`* XM4HLD2T_O`!00$TB`.@"P?``@9QBZ?``)9Q*Z?``,9PRZ?``-9P:Z?``*9@12G XMBV#8#!,`(&UZ#!,`(F8N4HL@2U*+$!!(@#H`9QX@2E**$(6Z?``B9A`,$P`BT XM9@12BV`&0BK__V`"8-9@."!+4HL0$$B`.@!G)KI\`"!G(+I\``EG&KI\``QG$ XM%+I\``UG#KI\``IG""!*4HH0A6#.($I2BD(02D5F`E.+4FR"[F``_UI"$D*G5 XM,"R"[E)`2,#E@"\`3KH$.BE`@NI03V8(0FR"[F``_MAZ`"9L@O)@)#`%2,#E< XM@"!L@NHABP@`($L@"$H89OR1P%.(,`A20$C`U\!21;IL@NYMUC`%2,#E@"!LV XM@NI"L`@`8`#^E"``,#Q__V`$,"\`#"!O``1*&&;\4T@B;P`(4T`0V5?(__QGY XM`D(0("\`!$YU3.\#```$(`@R+P`,8`(0V5?)__QG!E)!8`)"&%')__Q.=4Y5> XM``!(YPXP)&T`"$*G2'H`CDZZ`_XI0(+V4$]F"$S?#'!.74YU(&T`#")H`"0O! XM*0`$3KH$K"@`6$]G4DAZ`&T@1"\H`#9.N@1^)D!*@%!/9S1(>`/M+PM.N@(`U XM+`!03V<D(`;E@"H`($4E:``(`*0E1@"<2'@#[4AZ`#A.N@'<)4``H%!/+P1.D XMN@1*6$\O+(+V3KH"!$*L@O983V"`:6-O;BYL:6)R87)Y`%=)3D1/5P`J`$Y5N XM``!*K(+Z9P8@;(+Z3I`_+0`(3KH`"%1/3EU.=4Y5__PO!#`M``A(P"M`__Q*( XMK(+<9RAX`&`*/P1.N@#^5$]21+AL@)AM\#`L@)C!_``&+P`O+(+<3KH"Q%!/B XM2JR"_F<&(&R"_DZ02JR`GF<*+RR`GDZZ`4A83TJL@P)G""!L@P(@K(,&2JR#, XM"F<*+RR#"DZZ`5983TJL@PYG"B\L@PY.N@%&6$]*K(,29PHO+(,23KH!-EA/5 XM2JR#%F<*+RR#%DZZ`2983RQX``0(+@`$`2EG%"\-2_H`"DZN_^(J7V`&0J?S$ XM7TYS2JR"YF8P2JR"\F<H,"R"\$C`+P`O+(+R3KH"'#`L@NY20$C`Y8`O`"\L< XM@NI.N@((3^\`$&`.3KH!\B\L@N9.N@)N6$\@+?_\+FR"T$YU*!].74YU3E4`F XM`$CG#B`X+0`(,`3!_``&)$#5[(+<2D1M"KAL@)AL!$J29A`Y?``"@QIP_TS?U XM!'!.74YU""H`!P`$9@@O$DZZ``I83T*2<`!@XB(O``0L;(+83N[_W"(O``0LL XM;(+83N[_@BQL@MA.[O_*3.\`!@`$+&R"V$[N_^(L;(+83N[_Q$[Z``(B+P`$> XM+&R"V$[N_Z9(YP$$3.\@@``,+&R"U$ZN_Y1,WR"`3G5.^@`"(F\`!"QL@M1.4 XM[OYB3E4``$CG""!(>/__3KH`T"@`L+S_____6$]F"G``3-\$$$Y=3G5(>0`!D XM``%(>``B3KH`N"1`2H!03V8,+P1.N@#X<`!83V#6)6T`"``*%6T`#P`)%7P`Q XM!``(0BH`#A5$``]"ITZZ`*(E0``02JT`"%A/9PHO"DZZ`%I83V`*2&H`%$ZZ' XM`-!83R`*8)).50``+PHD;0`(2JH`"F<(+PI.N@#P6$\5?`#_``@E?/____\`" XM%'``$"H`#R\`3KH`?$AX`"(O"DZZ`%Y/[P`,)%].74YU(F\`!"QL@M1.[OZ>7 XM("\`!"QL@M1.[OZV3OH``DSO``,`!"QL@M1.[O\Z(F\`!"QL@M1.[OYZ3OH`? XM`B)O``0L;(+43N[^VD[Z``(L;(+43N[_?$[Z``(B;P`$("\`""QL@M1.[O\N5 XM("\`!"QL@M1.[OZP3OH``B!O``0L;(+43N[^C"!O``0@B%B00J@`!"%(``A., XM=4[Z``(L;(+4(F\`!"`O``A.[OW8+&R"U$[N_W9,[P,```0L;(+43N[^DB)O6 XM``0L;(+43N[^F$[Z``(B;P`$+&R"U$[N_H8@+P`$+&R"U$[N_L).^@`"(&\`G XM!"QL@M1.[OZ`(F\`!$SO``,`""QL@,1.[O\*(F\`!$SO``,`""QL@,1.[O\0J XM(F\`!$SO``\`""QL@,1.[O[.(F\`!"`O``@L;(#$3N[^JB)O``0@+P`(+&R`[ XMQ$[N_IY,[P,```0L;(+V3N[_H"!O``0L;(+V3N[_IB!O``0L;(+V3N[_LB!OL XM``0L;(#`3N[_RB!O``0L;(#`3N[_N"!O``0L;(#`3N[_-$SO`P``!"QL@,!.! XM[O[X(&\`!"QL@,!.[O[.(&\`!"QL@,!.[O[(```#[`````<````!```6A```5 XM%FH``!90```6-@``%AP``!4^```7/`````````/R```#Z@```"@````$``H`B XM"@)8`+0``0`(`QH``!(/```````````````F````````````9``C_____P`!) XM````.@``#>@```!````.L````$0```[X````20``#X0```!.```/[````%8`R XM``_^````70``$!````!B```0'````&H``!`^`````````````@`$``@`$``@. XM``$``0``$&@`%``````````````#[````!4``````````````!X````T````D XM.````#P```!`````1````$@```!,````4````%0```!8````7````&````!DH XL````:````&P```!P````=````'@```"2`````````_(```/K`````0```_)D_ X`` Xend Xsize 8504 SHAR_EOF echo "extracting aspline.fd" sed 's/^X//' << \SHAR_EOF > aspline.fd X/* given four control points (that's eight arguments) this draws a bspline */ X Xparse arg x1 y1 x2 y2 x3 y3 x4 y4 X Xlength = max(abs(x1-x4), abs(y1-74)) X Xif (length = 0) then exit X Xd1 = abs((x1 - x4) * (y2 - y4) - (y1 - y4) * (x2 - x4)) / length Xd2 = abs((x1 - x4) * (y3 - y4) - (y1 - y4) * (x3 - x4)) / length X X/* if they are colinear, simply draw a line. */ X Xif (d1 < 0.5) & (d2 < 0.5) then do X X address 'freedraw' 'Line ' trunc(x1) trunc(y1) trunc(x4) trunc(y4) X X/* otherwise invoke recursively on substrings */ X Xend Xelse do X X x12 = (x1 + x2) / 2 X y12 = (y1 + y2) / 2 X x23 = (x2 + x3) / 2 X y23 = (y2 + y3) / 2 X x34 = (x3 + x4) / 2 X y34 = (y3 + y4) / 2 X x123 = (x12 + x23) / 2 X y123 = (y12 + y23) / 2 X x234 = (x23 + x34) / 2 X y234 = (y23 + y34) / 2 X x1234 = (x123 + x234) / 2 X y1234 = (y123 + y234) / 2 X X address 'freedraw' 'Spawn aspline ' x1 y1 x12 y12 x123 y123 x1234 y1234 X address 'freedraw' 'Spawn aspline ' x1234 y1234 x234 y234 x34 y34 x4 y4 X Xend SHAR_EOF echo "extracting bspline.fd" sed 's/^X//' << \SHAR_EOF > bspline.fd X/* given four control points (that's eight arguments) this draws a bspline */ X Xparse arg x1 y1 x2 y2 x3 y3 x4 y4 X Xlength = max(abs(x1-x4), abs(y1-74)) X Xif (length = 0) then exit X Xd1 = abs((x1 - x4) * (y2 - y4) - (y1 - y4) * (x2 - x4)) / length Xd2 = abs((x1 - x4) * (y3 - y4) - (y1 - y4) * (x3 - x4)) / length X X/* if they are colinear, simply draw a line. */ X Xif (d1 < 0.5) & (d2 < 0.5) then do X X address 'freedraw' 'Line ' trunc(x1) trunc(y1) trunc(x4) trunc(y4) X X/* otherwise invoke recursively on substrings */ X Xend Xelse do X X x12 = (x1 + x2) / 2 X y12 = (y1 + y2) / 2 X x23 = (x2 + x3) / 2 X y23 = (y2 + y3) / 2 X x34 = (x3 + x4) / 2 X y34 = (y3 + y4) / 2 X x123 = (x12 + x23) / 2 X y123 = (y12 + y23) / 2 X x234 = (x23 + x34) / 2 X y234 = (y23 + y34) / 2 X x1234 = (x123 + x234) / 2 X y1234 = (y123 + y234) / 2 X X address 'freedraw' 'bspline ' x1 y1 x12 y12 x123 y123 x1234 y1234 X address 'freedraw' 'bspline ' x1234 y1234 x234 y234 x34 y34 x4 y4 X Xend SHAR_EOF echo "extracting sample.fd" sed 's/^X//' << \SHAR_EOF > sample.fd X/* draw something with freedraw */ X Xaddress 'freedraw' X Xcolor = 0 X Xdo i = 20 to 400 X 'Color ' color X color = color + 1 X if color > 3 then color = 0 X 'Line ' 20 i i 400 Xend i SHAR_EOF echo "extracting saspline.fd" sed 's/^X//' << \SHAR_EOF > saspline.fd X/* invokes aspline with some sample values */ X'aspline 314 159 10 9 456 44 536 141' Xexit SHAR_EOF echo "extracting sbspline.fd" sed 's/^X//' << \SHAR_EOF > sbspline.fd X/* invokes aspline with some sample values */ X'bspline 314 159 10 9 456 44 536 141' Xexit SHAR_EOF echo "extracting version.fd" sed 's/^X//' << \SHAR_EOF > version.fd X/* gets the version from freedraw */ X Xoptions results X Xaddress 'freedraw' 'Version' X Xsay result SHAR_EOF echo "End of archive 1 (of 1)" # if you want to concatenate archives, remove anything after this line exit