The 1.2.2a distribution is identical to the 1.2.2 distribution except for the inclusion of the Pascal interfaces, kindly provided by Erik Jensen . The readme from 1.2.2 continues.... The Bluchert Library version 1.2.2 10/13/95 KRB The Bluchert library allows communication with Igor Pro, using the PPC toolbox calls. You can get and send waves, send commands and get the results of those commands. NOTE: This version (1.2.2) has been tested by *one* (count 'em) beta tester. It seems to work, but I make no guarantee that it will not erase your hard disk, your neighbor's hard disk, and the hard disks of anyone you know who has a modem. "We'll call it 'QuickProtec'!" --Scott Adams. Be sure to save your experiments often, and back up regularly. (Good ideas in any case...) In the following text, whenever I say "Igor", you should assume I mean "Igor Pro", since vanilla Igor doesn't do PPC. Please send bug reports, feature requests, documentation laments, and good jokes to: Kevin R. Boyce Kevin.R.Boyce@gsfc.nasa.gov Code 663 NASA/GSFC Greenbelt, MD 20771 (301)286-3036 voice -1684 FAX TO USE FRAUBLUCHERT: -------------------- FrauBluchert is a quick-n-dirty application for testing and demonstrating the Bluchert library. With it you can send a wave (a noisy sine wave called "Jack", no adjustable parameters), get that wave (and do nothing with it!), send commands, and handle initializing etc. It's meant to show you how to interface with Bluchert, and it's only as sophisticated as I needed it to be for debugging. The "Find Igor" item in the Igor menu allows you to set the location of the local Igor application, the local experiment, and the remote copy of Igor. These are stored in a file called "FrauBluchert prefs" in your preferences folder. They are then used by the "Open local", "Open remote", and "Open local exp" items. You must "Initialize" before doing anything else. I have left all menu items enabled all the time so you can see what kinds of errors you get if you make calls in the wrong order. You don't actually have to use the "Shutdown" item; Bluchert always cleans up when it exits. (It patches ExitToShell, so even if it crashes you should usually be okay. If not, let me know.) You can only have one port open at a time. You will get an error if you try to open another one without closing the first one. The first time you try to open a port on another machine you will get not one but two authentication dialogs (where you enter your password). The first one is for sending the Apple Event to tell Igor Pro to open a PPC port, and the second is for opening the PPC port itself. Sorry, that's the way it is. At least it remembers your user data until you quit the application, so you only have to do it once even if you keep closing and opening the port. You can't use the "Bring Igor to front" item for a remote Igor (I don't think there's any easy way to do that). However, if you open a local port, then close it (and maybe open a remote port), "Bring Igor to Front" will still bring the local Igor to the front (as long as it's still running). I did this deliberately, but let me know if you think it's a bad idea. TO USE BLUCHERT: ---------------- (Written from a CodeWarrior C point of view.) 1. Add these files to your project: For 68K projects: Bluchert(4i)C.68k.lib - the Bluchert library ANSI(4i)C.68k.lib - Bluchert uses strcpy etc. MathLib68k.lib MacOS.lib AEObjectSupportLib.o Bluchert.rsrc - some resources Bluchert needs. For PPC projects: Bluchert.PPC.lib ANSI C.PPC.Lib MathLib ObjectSupportLib MWCRuntime.Lib InterfaceLib Bluchert.rsrc 2. Make sure that you don't have any resource IDs that conflict with the ones in Bluchert.rsrc. All its IDs are between 10000 and 11000, inclusive. 3. #include "Bluchert.h" in the files which make calls to Bluchert. "Bluchert.h" will itself #include "IgorPPC.h", which contains the definitions you'll need to send and receive waves. 4. make sure the "High-level event aware" bit is on in your SIZE resource. (For CodeWarrior, use the "SIZE flags" popup in the Project preferences page. In Think C, you set this from the "Set project type..." dialog.) This of course means your app needs to actually *be* high-level event aware. At a minimum, you need to "support" the 4 core AppleEvents: oapp, odoc, pdoc, and quit. Feel free to copy the AppleEvent code from the FrauBluchert demo app. It doesn't do much, but it should keep the system happy. Note that you *can* actually run with the high-level events bit set even if you don't install any AE handlers. However, you run the risk that someone (like the finder) will try to send you an AppleEvent, and then you're up the creek. The API uses pascal calling conventions, but you'll have to make your own header file if you use pascal. To use Bluchert, first call IppInit(). The next step will generally be to call IppFindIgor() and IppOpenLocal() if you want to talk to Igor on your own machine, or IppLocateIgorProcess() and IppOpen() if you want to talk over the network. You can then call IppSendWave(), IppGetWave(), and IppSendCommands() as you wish. If you want to keep the port open the entire time your app is running, you don't need to call IppClose() or IppShutdown(); Bluchert will clean up after itself when your app exits. IppClose() will close the port (local or remote), allowing you to open another. IppShutdown() kills the whole thing, requiring you to call IppInit() again. There's probably never any reason to call IppShutdown(). Okay, herewith, the API: pascal OSErr IppInit( void ); Call this first, to initialize Bluchert. It opens a PPC port whose name is "", where xxx is the name of your application. pascal short IppOpenLocal( FSSpec *igorFssP, FSSpec *expFssP ); Call this to open a PPC port to Igor on your machine. It returns either a valid "portRefNum" greater than 0, or a negative error code. As of version 2.04, Igor Pro only supports one port, and so does this version of Bluchert. (The portRefNum is always 1 or an error code.) If Igor Pro is not already running, it will try to open the file specified by igorFssP. You can use IppFindIgor() to keep track of Igor in a preferences file (using aliases, of course). If expFssP is not nil, the experiment it refers to will be opened. If it is nil, Igor will either continue with the current experiment (if it was already running) or open "Untitled" (if it was not running). Note that if the specified experiment is running, it will be closed and reopened. This can take a while for a big experiment. Until WaveMetrics adds a way to find the current experiment's name or FSSpec or something, that's the way it is. pascal OSErr IppOpenExpLocal( FSSpec *expFssP ); This allows you to open an experiment after you have opened Igor. It only works for local ports. If I can figure out how to make it work for remote ports, I'll do it, but for now this is all you get. pascal short IppOpen( TargetID *theTargetIDP, AliasHandle igorAlias, AliasHandle expAlias ); Call this to open a PPC port to Igor on any machine reachable by AppleTalk. theTargetIDP points to the copy of Igor you wish to communicate with. You can use IppLocateIgorProcess() to get a valid TargetID. The igorAlias and expAlias parameters are supposed to allow the remote copy of Igor to be automatically launched if it's not running. However, I know of no way to do that, so for now they are just ignored, and you can pass nil for both of them. This will also work if Igor Pro is on your own machine, but it won't launch it for you if it isn't already running. Use IppOpenLocal() if you want it to be launched for you. pascal OSErr IppSendWave( short portRefNum, IgorWaveBlock *waveP ); Use this to send a wave. The portRefNum parameter is there in case we start to support multiple open ports in the future. The IgorWaveBlock struct is described in "IgorPPC.h". It allows you to specify x and y units, scaling, the wave's length and the wave's name. Igor returns the wave name, which you can retrieve using IppGetLastReply(). It should be the same as the name you specified in waveP. pascal OSErr IppGetWave( short portRefNum, const char *name, Handle data, IgorWaveHeader *hdr, short *numType ); This gets a wave for you. Sorry it's not symmetric with IppSendWave. name is the name of the wave you want, data is a handle where the wave will go (the handle will be resized to match the wave), hdr is filled in with info about the wave, and numType gets filled in with the number type of the wave (NT_FP32, NT_FP64, etc). The typedefs and #defines used here are all defined in IgorPPC.h, which is #included by Bluchert.h. pascal OSErr IppSendCommands( short portRefNum, char *commands, IgorPPCCmdReturn *rsp, Handle histH, Handle retDataH ); This sends commands and waits for data to be returned. Someday I may implement an asynchronous version so you can continue doing things while Igor executes the commands, but for now you just have to wait. Also, it will time out if the commands take too long. commands is the null-terminated command string (can be arbitrarily long), rsp (see IgorPPC.h) gets filled with various useful bits like whether Igor encountered an error while processing the commands. histH and retDataH need to be genuine Handles, as gotten from NewHandle(). They will be filled (after appropriate resizing) with the new stuff added to the history area and the returned data (if any), respectively. That is, if you send fprintf 0, "blah = %f", V_blah or fBinWrite 0, V_blah or savePict as "PPCpacket" then you will get back the data (text, a float, or a PICT, respectively), in retDataH. It's up to you to interpret the data. The demo app FrauBluchert displays it as a hex dump (which is really long for a PICT!) Note that errors are handled slightly differently for this call. If Igor returns an error, IppSendCommands will NOT return an error. Therefore you need to check rsp->igorErr to find out whether Igor encountered an error while processing your commands. The return code from IppSendCommands indicates only whether there was a problem transferring the commands to Igor. pascal OSErr IppSaveExp( short portRefNum ); This saves the current experiment. It works by sending the "Save" AppleEvent, in the kAENeverInteract mode. This means that if the current experiment is "Untitled", you will get an error. pascal OSErr IppClose( short portRefNum ); This closes the given port (again, the only valid portRefNum so far is 1). It works for local or remote ports. pascal OSErr IppShutdown( Boolean saveExp, Boolean quitIgor ); This shuts down PPC. If quitIgor is true and saveExp is false, and the experiment is modified, Igor will put up the "Save experiment?" dialog. If you forget to call this, Bluchert should do it for you when your program exits (it patches ExitToShell). But you might want to stop PPC before exiting your program, so this call is here. pascal OSErr IppStatus( short portRefNum, ippStatusBlock *sb ); This is supposed to give you some status information, but it's still not implemented. pascal OSErr IppSetUpdateProc( UpdateProcPtr updateProc ); While waiting for a reply, the PPC toolbox repeatedly calls an update procedure. You can register your update procedure with this call, and then you won't have to have ugly blank spots in your windows while waiting for a command to complete. If you don't call this, or call it with nil as the argument, no update procedure will be called. The definition of UpdateProcPtr is in Bluchert.h. If you have strict prototype checking on, the compiler won't let you define your update procedure incorrectly. pascal OSErr IppBringIgorToFront( void ); This tells Igor to come to the front. It won't actually do so until the next time you call WaitNextEvent(). pascal short IppGetVersion( void ); Returns 100 times Bluchert's version number (not Igor's). E.g. for version 1.2.3, you would get 123 back. pascal OSErr IppFindIgor( FSSpec *prefFSS, OSType fType, OSType fCreator, short aliasID, FSSpec *igorFSS, AliasHandle igorAlias, Boolean alwaysAsk ); Use this to locate a copy of Igor to run. It will attempt to find an 'alis' resource with an ID of aliasID in the "prefs" file specified by prefFSS. If it finds it, it will resolve the alias and return the resulting filespec in igorFSS. If igorAlias is not nil, it will also copy the alias to igorAlias (after resizing the Handle appropriately). If the alias is missing or damaged or can't be resolved, it will put up a StandardGetFile dialog so the user can find Igor, and the result will be saved in the "prefs" file. If the prefs file is missing entirely, it will be created, with the type and creator specified by fType and fCreator. See the demo app for an example of how to put it in the preferences folder. If alwaysAsk is true, it will put up the StandardGetFile dialog whether or not the alias resource is found. Use this method when you want to change to a different copy of Igor. pascal OSErr IppFindExp( FSSpec *prefFSS, OSType fType, OSType fCreator, short aliasID, FSSpec *expFSS, Boolean alwaysAsk ); This is the same as IppFindIgor, except that it finds an experiment instead. pascal OSErr IppLocateIgorProcess( FSSpec *prefFSS, OSType fType, OSType fCreator, short targIDID, TargetID *theTargetID, Boolean alwaysAsk ); Use this to find a remote copy of Igor to communicate with. As with IppFindIgor() and IppFindExp(), it looks in the file specified by prefFSS, and if the file is not found, it creates it, using the type and creator specified by fType and fCreator. If it finds the file, it looks for a resource of type='TARG', ID=targIDID. If it finds that, it copies it into theTargetID. You need to make sure theTargetID points to an area at least as large as sizeof(TargetID). e.g. pass the address of an actual TargetID variable. If the prefs file is missing or damaged, or if alwaysAsk is true, this routine will put up the PPCBrowser dialog box, allowing you to choose which copy of Igor Pro you want to talk to. It will only show Igor Pro, and of course only copies which are actually running. pascal char *IppGetLastReply( short portRefNum ); This returns a pointer to the text of the most recent reply from Igor. This will generally contain error text (if there has been an error), although some routines supply information. (See IppSendWave, for example.) pascal OSErr IppDecodeError( OSErr err, char *buf ); Use this to convert an error code into text. It handles the most common OS errors (memory, file, apple event and PPC errors), and most of the errors Igor might return. Buf should be 512 characters long. TO MODIFY BLUCHERT: ------------------- Bluchert is now made under CodeWarrior (7.0). It compiles under both the PowerPC and the 68K environments. There are two projects: Bluchert.68K.µ and Bluchert.PPC.µ. You should be able to just hack away to your heart's content, and then "Make". As far as I can tell, CodeWarrior always puts the result of a Make in the same folder as the project, so you'll have to move the library ("Bluchert(4i).68K.lib" or "Bluchert.PPC.lib") to your library folder by hand. If you change the numerical representation in the 68K library, you should change the name of the library to be consistent with Metrowerks' conventions (e.g. Bluchert(2i/8d).68K.lib). If you want to recompile FrauBluchert, you will need the TransSkel library and headers. I haven't included it, to save space. You can get it from: gopher://indri.primate.wisc.edu/11/mac/transskel/ VERSION HISTORY: ---------------- 1.2.2 Modified to work with OLDROUTINENAMES undefined. Works with CW7. 1.2.1 Realized I could compile the PowerPC version even on my 68K Mac, so now it compiles without complaint. Haven't actually *run* it on a PowerPC yet... 1.2.0 Switched to MetroWerks CodeWarrior 6.1, and the Universal Headers. Someone try this on a PowerPC! Not publically released. 1.1.1 Changed from "AEObject Support" library to OSL library. 1.1.0 Added network support.