Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

The Macintosh Communications Toolbox

Source Code Accompanies This Article. Download It Now.


DEC90: THE MACINTOSH COMMUNICATIONS TOOLBOX

THE MACINTOSH COMMUNICATIONS TOOLBOX

Writing comm applications that are terminal, file transfer, and connection independent

This article contains the following executables: CHEAPCOM.R CHEAPCOM.MAK CHEAPCOM.C CHEAPCOM.H

Don Gaspar

Don is a software engineer at Apple Computer. He can be reached at Apple Computer, 20525 Mariani Ave., MS-35Q, Cupertino, CA 95014. Don was working for Dialog Information Services at the time he wrote this article.


A long with standardized communications functions, Apple's Macintosh Communications Toolbox provides an entire spectrum of dynamic tools to aid you in constructing dynamic communications applications. Programs that make use of the Comm Toolbox become completely terminal, file transfer, and connection independent. If you have to add any new functions and if your program is written properly, your communications program will be extended with the new functionality. The Comm Toolbox does this via tools kept in the System Folder that offer independence for the communication applications.

Before putting the Comm Toolbox to work for you, become familiar with the Connection Manager, Terminal Manager, and File Transfer Manager. (A fourth key manager -- the Communications Resource Manager -- will not be discussed in the context of this article.) As Figure 1 illustrates, these managers interact with specific tools that work directly with the ROM OS. The three managers expand the functionality of the Macintosh Toolbox by offering functions for any type of application that involves communications. The managers are simple to use, and their functions resemble those of the other toolbox managers you've already been using for years. If you've used the Memory Manager, TextEdit, QuickDraw, the Window Manager, and some of the others, you're ready to go.

The Connection Manager

The Connection Manager is a medium through which you tell the Comm Toolbox what type of physical connection is between your Mac and the device it's talking to. This "other" device might be a modem, a direct serial connection, ADSP, perhaps a software MNP level 5 or X.25 tool, or maybe something that hasn't even been thought of yet. No problem. Just write your own connection tool and anyone who writes a program using the Comm Toolbox will be able to use your new hardware connection as well.

For instance, once the user selects the type of connection being used (or sets a default), special preferences can then be made for that particular connection. If using a modem tool, the user should be able to tell the program what type of modem is being used, the phone number to dial, baud rate, stop bits, parity, and so on.

The function that enables this parameter setting is CMChoose( ). When called, the Comm Toolbox displays a dialog box (see Figure 2) that allows the user to change settings. This is the standard dialog box offered through the Comm Toolbox; you don't have to use it if you don't want to. You can override anything you don't like, then design and use your own. For consistency across communications programs, however, it's recommended you stick with the standard so that someone unfamiliar with your program will understand how to use it.

The Terminal Manager

The Terminal Manager tells the Comm Toolbox what type of emulation is taking place: TTY, VT-100, VT-320, or even IBM 3278. With the Comm Toolbox, you just add a terminal tool with the emulation you want in the system folder; your program then becomes terminal independent. Apple has implemented several of these emulations, including VT-320. You can use the ones that already exist, license more from a third party, or write your own.

The Terminal Manager's counterpart to the Connection Manager's CMChoose( ) is TMChoose( ); the dialog changes according to what type of terminal is selected and allows the user to change their preference for the particular terminal they are using (the tool controls this).

The File Transfer Manager

The File Transfer Manager allows you to implement standard file transfer (Kermit, XModem, ZModem, text transfer, and so on) functions with minimal effort, for file transfer independence. Suppose, for instance, that next year a new file transfer scheme is implemented on CompuServe. What would happen to your program? Would you have to send users new disks with the new file transfer added to it? Not at all. Users simply add the new file transfer tool to their System Folder, and because your program is file transfer independent, it can automatically use the new tool without any modifications. In fact, the current file transfer tool can be used to get the new one.

A Sample Comm Toolbox Application

In this section, I'll briefly describe a communications application I call "CheapCom" (see Listing One, page 94). This program, written using MPW C 3.1, illustrates how the Comm Toolbox managers and other functions are implemented to easily build a full-featured communications program that supports connection, terminal emulation, and file transfer. Because of space constraints, I haven't included any of the standard Macintosh user interface features in Listing One; the code presented is the communications core. However, the complete system (including resource files), as well as just the core code, is available online. To compile and use the program, you'll need the Comm Toolbox installed on your Macintosh, MPW with the C compiler Version 3.1 or later, and the Comm Toolbox Interface. (Contact APDA, Apple Computer Inc., 20525 Mariani Ave., Cupertino, CA 95014, for Comm Toolbox specifics.)

To start off, you have to initialize the communication managers just as you do the Window Manager, TextEdit, QuickDraw, and the others. A typical initialization function might resemble that in Figure 3. For compatibility reasons, always initialize the Comm Toolbox in the order shown in Figure 3. You should also always check for existence of the Comm Toolbox before initializing it, otherwise the error codes returned will be meaningless. (If the user is not using System 7.0, then the Comm Toolbox must be installed via a special INIT, available from APDA.) If the Comm Toolbox isn't there when you start up, your program will either have to disable its communications functionality or it will have to just quit (after notifying the user, of course).

Figure 3: Initializing the Comm Toolbox

  InitGraf((Ptr) &qd.thePort);             // QuickDraw
  InitFonts();                             // Font Manager
  InitWindows();                                          // Window Manager
  InitMenus();                             // Menu Manager
  TEInit();                                // Text Edit
  InitDialogs(nil);                        // Dialog Manager
  InitCursor();                            // starting cursor

  err = InitCTBUtilities();                // Comm Toolbox Utilities
  err = InitCRM();                         // Comm Toolbox Resource Manager
  err = InitTM();                          // Terminal Manager
  If (err == tmNoTools)
               AlertUser("No terminal tools found\0", true);
  err = InitCM();                          // Connection Manager
  if (err == cmNoTools)
               AlertUser("No connection tools found\0", true);
  err = InitFT();                          // File Transfer Manager
  if (err == ftNoTools)
               AlertUser ("No file transfer tools found\0", false);

Next, you should check for tools. If no tools are available, the value "xxNoTools" will be returned; you might want to quit your program in that case. For example, if no connection tools were available, it would be physically impossible to establish a hardware connection. So you must alert the user and quit the application. However, if no file transfer tools were available, rather than quit the program you might simply alert the user that the program couldn't find any and disable the user's ability to select functions that deal with file transfer.

Then, you need to make a new terminal record, connection record, and file transfer record. Code like that shown in Figure 4 accomplishes this. The calls TMNew( ), CMNew( ), and FTNew( ) set up the appropriate records for your application to start with communications; these calls resemble TENew( ) From TextEdit, and will be familiar to Mac programmers. You also have to set up a buffer at this point, since the Comm Toolbox must have access to it here.

Figure 4: Code to make a new terminal, connection, and file transfer record

  gTerm = TMNew (&theRect,&theRect, tmSaveBeforeClear + tmAutoScroll,
                          procID, window, (ProcPtr) TermSendProc, (ProcPtr)
                          cacheProc,nil,
                         /* (ProcPtr) clikLoop*/nil,
                         (ProcPtr)ToolGetConnEnvirons,0,0);
  if (gTerm == nil)
             AlertUser("Can't create a terminal tool/0", true);
             HLock ((Handle)gTerm);

  /* connection tool */
  procID = FindToolID(classCM);
  if (procID == -1)       // get out of here if no tools!
             AlertUser("No connection tools found/0", true);

  sizes[cmDataIn] = kBufferSize;        // just data channel; large
                                        incoming buffer
  sizes[cmDataOut] = kBufferSize;
  sizes[cmCnt1In] = 0;
  sizes[cmCnt1Out] = 0;
  sizes[cmAttnIn] = 0;
  sizes[cmAttnOut] = 0;

  gConn = CMNew(procID, cmData, sizes, 0,0);
  if(gConn == nil)
            AlertUser("Can't create a connection tool/0", true);
  HLock ((Handle)gConn);

  /* allocate space for reads/writes using number returned by connection
  tool */
  gBuffer = NewPtrClear(sizes[cmDataIn]);
  if (MemError() != noErr)
             AlertUser("Out of memory\0", true);

  /* file transfer tool */
  procID = FindToolID(classFT);
  if (procID == -1)
             AlertUser("No file transfer tools found\0", false);

  /* no read/write proc -- tool has its own */
  gFT = FTNew(procID, 0 ,(ProcPtr)FTSendProc, (ProcPtr)FTReceiveProc,
            nil, nil, (ProcPtr)ToolGetConnEnvirons, window, OL,OL);

  if(gFT == nil)
            AlertUser("Can't create a file transfer tool\0", true);

Up to this point, all we're doing is setting up the Comm Toolbox to our specification. However, there's a lot going on in the background. For example, if we didn't want the Comm Toolbox to automatically handle the terminal emulation window and instead create some iconic wonderland for users when we call TMNew with tmSaveBeforeClear + tmAutoScroll, we could add tmInvisible to tell the Comm Toolbox we're handling our own emulation appearance. Since there's much more you can do, you should read Inside the Communications Toolbox (the manual that accompanies the Comm Toolbox) for more information.

The Idle Function

When you write a communications program, you usually set up some type of timer to read from the serial port at a timed interval. This prevents hardware overwrites of incoming data. Then you display the data in a window on your screen by reading periodically, possibly every nth time through your event loop. In order to handle file transfers, emulation, and data arriving via your comm buffer, you have to read this data and figure out exactly where it goes; the Comm Toolbox aids you with the functions TMIdle( ), CMIdle( ), and FTIdle( )--these functions assist you in delegating the data to the appropriate managers. The function DoIdle( ) (see Listing One) takes care of this; please take a look at it, since yours will have to be nearly identical. This function updates the file transfer status; the terminal emulation is handled by the Comm Toolbox.

You still have to provide your own customization and buffer. The Comm Toolbox only buffers the terminal emulation window; when the data goes off the screen, you'll have to save it to your global buffer. (Refer to the function cacheProc( ) in the source code for an example.) When the data reaches the region that is outside of the terminal emulation region, cacheProc( ) is called; at this point, you would have to either add the data (one line at a time) to TextEdit, or add it to your own linked-list of text that you maintain yourself. Your own linked-list of data objects is preferable, since TextEdit is slow and limited.

Other Modifications

There are a lot of simple things common sense will tell you need to be added to your program. For example, you need to answer questions like "Where does the Terminal Manager get updates and key events?"

Your standard update loop will have to be able to notify the Terminal Manager that it needs to update its emulation region on the screen. You can do this by verifying the terminal record's integrity (not NIL and not odd) and then calling TMUpdate( ). This is pretty simple and allows your program to add communications without deviating significantly from a standard Macintosh program.

Suppose the user is typing on the keyboard. That action (event) will have to be translated if terminal emulation is taking place. You'll have to add a couple of lines which make a call to TMKey( ) to the function that handles normal key events, so that the Terminal Manager will be able to handle such events.

There are several other similar Terminal Manager functions to look at; again, refer to Inside the Communications Toolbox. Functions such as TMResize( ), TMClick( ), and TMScroll( ) will be devoted to the user selecting text (or objects) within the terminal emulation region of the screen, scrolling (so that data is not lost), resizing the window (so the emulation region is affected), and so on.

If your communications window (perhaps one of several windows your program is using) is in the background and is then activated by the user clicking on it, you address this activate event, bring your window to the front, and call SetPort( ) so that the Mac will know which grafPort to draw in. Everything is pretty much the same here, except that you might want to add the calls TMActivate( ), CMActivate( ), and FTActivate( ) so that the three managers can activate their-functions appropriately.

Cleaning up when your program is complete requires that you cancel the functions of the three managers and dispose of your buffer. We can use the Memory Manager function DisposePtr( ) to get rid of our buffer, but what about the records for the Connection Manager, Terminal Manager, and the File Transfer Manager? Just use the routines the Comm Toolbox provides: CMDispose( ), TMDispose( ), and FT Dispose( ). Adding these calls should make everything look like a regular Mac program.

Installation

If you're still working with System 6, you'll have to install the Comm Toolbox (it's not built-in to 6) and create a new folder within the System Folder called "Communications" to put all of your tools in.

If you're distributing an application you built with System 6, you'll also need to offer your customers an installation disk. You can get this, along with several connection, terminal emulation, and file transfer tools from Developer Services at Apple.

Acknowledgments

The author is indebted to Mark Baumwell, James Benninghaus, Veronica Dullaghan, and Alex Kazim, all from Apple. They provided a substantial amount of technical support and help when needed.

_THE MACINTOSH COMMUNICATIONS TOOLBOX_ by Don Gaspar

[LISTING ONE]

<a name="0264_000f">

/* Cheap Com  by Don Gaspar */
/* Requires MPW C and the Comm Toolbox. The complete program, including */
/* the resource file, header file, and make, are available electronically. */
/* These are the standard Mac includes for all managers */

#include <values.h>
#include <types.h>
#include <Resources.h>
#include <QuickDraw.h>
#include <fonts.h>
#include <events.h>
#include <windows.h>
#include <menus.h>
#include <textedit.h>
#include <dialogs.h>
#include <Controls.h>
#include <desk.h>
#include <toolutils.h>
#include <memory.h>
#include <Lists.h>
#include <SegLoad.h>
#include <Files.h>
#include <Packages.h>
#include <OSEvents.h>
#include <OSUtils.h>
#include <DiskInit.h>
#include <Traps.h>
#include <String.h>
#include <Strings.h>

#include <CRMIntf.h>  // Communications Resource Manager stuff
#include <CMIntf.h>   // Connection Manager stuff
#include <FTIntf.h>   // File Transfer Manager stuff
#include <TMIntf.h>   // Terminal Manager stuff
#include <CTBUtils.h> // Communications Toolbox Utility stuff

#include "CommTypes.h"    // communications types, etc.
#include "CheapComm.h"    // constants, forward declarations, etc.

/*  global variables */
Boolean      gHasWaitNextEvent;   // does user's machine have WaitNextEvent?
Boolean      gInBackground;   // are we in the background?
Boolean      gStopped;      // are we stopped?

TermHandle   gTerm;           // handle to terminal record
ConnHandle   gConn;         // handle to connection record
FTHandle     gFT;         // handle to file transfer record

Ptr        gBuffer;        // global connection buffer
long       gFTSearchRefNum;
Boolean     gStartFT;        // are we doing a file transfer?
Boolean     gWasFT;          // was a file transfer in progress?

short       gDummy;
Handle       gCache;        // buffer for last terminal line received/sent
TEHandle    gTE;       // buffer for terminal emulator
ControlHandle gScrollHHandle, gScrollVHandle;

#pragma segment Main
/*   Sends data out via choosen connection */
pascal long   TermSendProc(thePtr, theSize, refCon, flags)
   Ptr   thePtr;
   long   theSize;
   long   refCon;
   short   flags;
{
   CMErr   theErr;
   long   termSendProc = 0L;
   if (gConn != nil) {
             theErr = CMWrite(gConn,thePtr,&theSize,cmData,false,nil,0,flags);
      if (theErr == noErr)
         termSendProc = theSize;
   }
   return(termSendProc);
}
#pragma segment Main
/*  Gets the data from the connection tool and sends it to the terminal tool */
pascal void TermRecvProc()
{
   CMErr      theErr;
   CMStatFlags   status;
   CMBufferSizes   sizes;
   short      flags;
   if (gConn != nil && gTerm != nil) {
      theErr = CMStatus(gConn, sizes, &status);
      if (theErr == noErr) {
if ((status & (cmStatusOpen+cmStatusDataAvail)) != 0 && sizes[cmDataIn] != 0) {
            if (sizes[cmDataIn] > kBufferSize)
               sizes[cmDataIn] = kBufferSize;
theErr=CMRead(gConn, gBuffer, &sizes[cmDataIn], cmData, false, nil, 0, &flags);
        if (theErr  == noErr)   // give it to terminal emulation buffer
       sizes[cmDataIn] = TMStream(gTerm, gBuffer, sizes[cmDataIn], flags);
      }
   }
   else
   ;            // Connection Manager will handle this
    }
}
#pragma segment Main
/* Gets the connection environments for FT or Term tool */
pascal OSErr   ToolGetConnEnvirons(refCon, theEnvirons)
   long   refCon;
   ConnEnvironRec   *theEnvirons;
{
   OSErr   toolGetConnEnvirons = envNotPresent;
   if(gConn != nil)
      toolGetConnEnvirons = CMGetConnEnvirons(gConn,theEnvirons);
   return(toolGetConnEnvirons);
}
#pragma segment Main
/* Sends data during a file transfer */
pascal long   FTSendProc(thePtr, theSize, refCon, channel, flags)
   Ptr      thePtr;
   long      theSize;
   long      refCon;
   CMChannel  channel;
   short      flags;
{
   CMErr   theErr;
   long   ftSendProc = 0L;
    if (gConn != nil) {
      theErr = CMWrite(gConn, thePtr,& theSize, channel, false, nil, 0, flags);
      if(theErr == noErr)
         ftSendProc = theSize;
   }
   return(ftSendProc);
}
#pragma segment Main
/* Gets the data during a data transfer */
pascal long   FTReceiveProc(thePtr, theSize, refCon, channel, flags)
   Ptr      thePtr;
   long      theSize;
   long      refCon;
   CMChannel  channel;
   short      *flags;
{
   CMErr      theErr;
   long      ftReceiveProc = 0L;
     if (gConn != nil) {
       theErr = CMRead(gConn, thePtr, &theSize, channel, false, nil, 0, flags);
      if (theErr == noErr)
         ftReceiveProc = theSize;
   }
   return(ftReceiveProc);
}
#pragma segment Main
/* Sets file transfer flag if an autoreceive string was found */
pascal void AutoRecCallBack(theConn, data, refNum)
   ConnHandle     theConn;
   Ptr          data;
   long          refNum;
{
   if (gFTSearchRefNum == refNum)
      gStartFT = true;
}
#pragma segment Main
/* Checks if file transfer has autoreceive string; adds a search to find it */
void AddFTSearch()
{
   Str255   tempStr;
   if (gFT != nil && gConn != nil) {
      //tempStr = (*gFT)->autoRec;
   if ((*gFT)->autoRec[0] != 0) {
    gFTSearchRefNum = CMAddSearch(gConn,(*gFT)->autoRec, cmSearchSevenBit,
            (ProcPtr)AutoRecCallBack);
         if (gFTSearchRefNum == -1) {
                   AlertUser("Couldn't add stream search\0", false);
            gFTSearchRefNum = 0;
         }
      }
   }
}
#pragma segment Main
/* Initiates a file transfer send from menu command */
void DoSend()
{
   SFReply    theReply;
   Point      where;
   short      numTypes;
   SFTypeList   typeList;
   FTErr      anyErr;
   if(gFT != nil) {
      SetPt(&where,100,100);

      if(((**gFT).attributes & ftTextOnly) != 0) {
         typeList[0] = 'TEXT';
         numTypes = 1;
      }
      else
         numTypes = -1;
    sfgetfile(&where, "File to send", nil, numTypes, typeList, nil, &theReply);
      if(theReply.good) {
         anyErr = FTStart(gFT, ftTransmitting, &theReply);
         if(anyErr != noErr)
            ;  // file transfer tool will alert user
      }
   }
}
#pragma segment Main
/* Initiates a file transfer receive from menu */
void DoReceive()
{
   SFReply   theReply;
   OSErr      anyErr;

   if (gFT != nil) {
      theReply.vRefNum = 0;
      //theReply.fName = '';
      gStartFT = false;
      if (gConn != nil ) {
         if ((**gFT).autoRec != "\0" && gFTSearchRefNum != 0) {
            CMRemoveSearch(gConn, gFTSearchRefNum);
            gFTSearchRefNum = 0;
         }
      }
      anyErr = FTStart(gFT, ftReceiving, &theReply);
      if(anyErr != noErr)
         ;  // file transfer tool will alert user
   }
}
#pragma segment Main
/* Initiates a connection */
void OpenConnection()
{
   CMErr      theErr;
   CMBufferSizes   sizes;
   CMStatFlags   status;
   if(gConn != nil) {
      theErr = CMStatus(gConn, sizes, &status);
      if(theErr == noErr)
         if((status & (cmStatusOpen + cmStatusOpening)) == 0)
            theErr =CMOpen(gConn, false, nil, -1);
         if (theErr != noErr)
           ;  // connection tool will tell user if there's an errror
   }
}
#pragma segment Main
/* Cancels connection */
void CloseConnection()
{
   CMErr      theErr;
   CMBufferSizes   sizes;
   CMStatFlags   status;
   if (gConn != nil) {
      theErr = CMStatus(gConn, sizes, &status);
      if(theErr == noErr)
         if ((status & (cmStatusOpen+cmStatusOpening)) != 0)
            theErr = CMClose(gConn, false, nil ,0, true);
      if (theErr != noErr)
         ; // connection tool will handle error
   }
}
#pragma segment Main
/* tries to get default tool proc ID, otherwise gets first one it can find */
short   FindToolID(toolClass)
   OSType   toolClass;
{
   Str255   toolName;
   OSErr   anyErr;
   short   procID = -1;
   if (toolClass == classTM) {
      StuffHex(&toolName,kDefaultTermTool);
      procID = TMGetProcID(toolName);
      if(procID == -1) {
         anyErr = CRMGetIndToolName(toolClass,1, toolName);
         if (anyErr == noErr)
            procID = TMGetProcID(toolName);
      }
   }
   else if (toolClass == classCM) {
      StuffHex(&toolName,kDefaultConnTool);
      procID = CMGetProcID(toolName);
      if(procID == -1) {
         anyErr = CRMGetIndToolName(toolClass,1, toolName);
         if (anyErr == noErr)
            procID = CMGetProcID(toolName);
      }
   }
   else if (toolClass == classFT) {
      StuffHex(&toolName,kDefaultFTTool);
      procID = FTGetProcID(toolName);
      if(procID == -1) {
         anyErr = CRMGetIndToolName(toolClass,1, toolName);
         if (anyErr == noErr)
            procID = FTGetProcID(toolName);
      }
   }
   return(procID);
}
#pragma segment Main
/* this is click loop for terminal emulation to track */
pascal Boolean clikLoop(refcon)
   long   refcon;
{
   return(true);
}
#pragma segment Initialize
/* this function sets up CommToolbox for what we need;
   it should resemble most other Macinotosh toolbox calls   */
void InitCommTB()
{
   (void)InitCTBUtilities();   //   Comm Toolbox Utilities
   (void)InitCRM();      //   Communications Resource Manager
   //   initialize the Terminal Manager
   if (InitTM() == tmNoTools)         //   Did we fail?
      AlertUser("No terminal tools found\0", true);
   //   Initialize the Connection Manager
   if(InitCM()== cmNoTools)         //   failure?
      AlertUser("No connection tools found\0", true);
   //   Initialize the File Transfer Manager
   if(InitFT() == ftNoTools)            // failure?
      AlertUser("No file transfer tools found\0",false);
   gTerm = nil;                    // initialize our globals
   gConn = nil;
   gFT = nil;
   gCache = nil;
   gFTSearchRefNum = 0;
}
#pragma segment Main
/*   this will cache all data coming in through serial port */
pascal long cacheProc(refCon, theTermData)
   long      refCon;
   TermDataBlock   *theTermData;
{
   long      sizeCached;
   TermEnvironRec   theEnvirons;
   theEnvirons.version = curTermEnvRecVers;
   theEnvirons.termType = tmTextTerminal;
   (void)TMGetTermEnvirons(gTerm, &theEnvirons);
   if (theTermData->theData == nil)
      return(-1);
   if(gCache != nil)   //   is it valid?
         DisposHandle(gCache);
   HLock((Handle)theTermData->theData);
   gCache = theTermData->theData;
   if(HandToHand(&gCache)) {
         DisposHandle(gCache);
         sizeCached = -1;
      }
      else {
         sizeCached = GetHandleSize(gCache);
      }
   HUnlock((Handle)theTermData->theData);
   if(theTermData->flags == tmTextTerminal && sizeCached >0L) {
      /*HandAndHand(gCache, (**gTE).hText);
      (**gTE).teLength += 80;
      (**gTE).nLines += 1;*/
      ((Ptr)*gCache,80L,gTE);
      //(**gTE).viewRect.top -= (**gTE).lineHeight;
      //(**gTE).destRect.top -= (**gTE).lineHeight;
      //TECalText(gTE);
      //TEScroll(0,-(**gTE).lineHeight,gTE);
   }
   return(tmNoErr);
}
#pragma segment Main
/* gets window and create session */
Boolean DoNewWindow()
{
   WindowPtr   window;
   Rect      theRect;
   short      procID;
   CMBufferSizes   sizes;
   Rect      tempRect;
   short      index;
   Rect      r;
   window = GetNewWindow(rWindow, nil, (WindowPtr)-1);
   SetPort(window);
   /* no cache, breakproc, or clikloop */
   gTerm = TMNew(&theRect,&theRect, tmSaveBeforeClear + tmAutoScroll,
       procID, window, (ProcPtr)TermSendProc,(ProcPtr)cacheProc,nil,
      /*(ProcPtr)clikLoop*/nil, (ProcPtr)ToolGetConnEnvirons,0,0);
   SetRect(&r,theRect.left,-theRect.bottom,theRect.right,theRect.top);
   gTE = TENew(&r,&r);
   (**gTE).txSize = 9;
   (**gTE).txFont = monaco;
   (**gTE).viewRect.bottom = (((**gTE).viewRect.bottom - (**gTE).viewRect.top)/
   (**gTE).lineHeight)*(**gTE).lineHeight + (**gTE).viewRect.top;
   TEAutoView(true,gTE);
   /* custom configure with personal settings -- store as a file later */
   (void)TMSetConfig(gTerm, "Scroll Smooth\0");
   if(gTerm == nil)
      AlertUser("Can't create a terminal tool\0", true);
   HLock((Handle)gTerm);
   /* connection tool */
   procID = FindToolID(classCM);
   if(procID == -1)
      AlertUser("No connection tools found/0", true);
      sizes[cmDataIn] = kBufferSize*10;  // data channel; large incoming buffer
      sizes[cmDataOut] = kBufferSize;
      sizes[cmCntlIn] = 0;
      sizes[cmCntlOut] = 0;
      sizes[cmAttnIn] = 0;
      sizes[cmAttnOut] = 0;
   gConn = CMNew(procID, cmData, sizes, 0,0);
        (void)CMSetConfig(gConn,"Baud 9600, Bits 7, StopBits 1, Parity Even,
                      ModemType Other, PhoneNumber \0429,1800-346-0145\042\0");
   if(gConn == nil)
      AlertUser("Can't create a connection tool/0", true);
   HLock((Handle)gConn);
/* allocate space for reads/writes using number returned by connection tool */
   gBuffer = NewPtrClear(sizes[cmDataIn]);
   if(MemError() != noErr)
      AlertUser("Out of memory\0", true);
   /* file transfer tool */
   procID = FindToolID(classFT);
   if(procID == -1)
      AlertUser("No file transfer tools found\0", false);
   /* no read/write proc -- tool has its own */
   gFT = FTNew(procID, 0 ,(ProcPtr)FTSendProc, (ProcPtr)FTReceiveProc,
      nil, nil, (ProcPtr)ToolGetConnEnvirons, window, 0L,0L);
   if(gFT == nil)
      AlertUser("Can't create a file transfer tool\0", true);
   HLock((Handle)gFT);
   gWasFT = false;
   gStartFT = false;
   gFTSearchRefNum = 0;
   AddFTSearch();
   return(true);
}
#pragma segment Main
/* Updates the window */
void DoUpdate(window)
   WindowPtr   window;
{
   RgnHandle   savedClip;
   GrafPtr    savedPort;

   if (IsAppWindow(window)) {
      GetPort(&savedPort);
      SetPort(window);
      /* clip to the window content */
      savedClip = NewRgn();
      GetClip(savedClip);
      ClipRect(&window->portRect);
      DrawControls(window);
      DrawGrowIcon(window);
      BeginUpdate(window);
         if(gTerm != nil )
             TMUpdate(gTerm, window->visRgn);
         if(gTE != nil)
            TEUpdate(&window->portRect,gTE);
      EndUpdate(window);
      SetClip(savedClip);
      DisposeRgn(savedClip);
      SetPort(savedPort);
   }
}
#pragma segment Main
/* suspends/resumes terminal window */
void DoResume(becomingActive)
   Boolean becomingActive;
{
   WindowPtr   theWindow;
   GrafPtr    savedPort;
   GetPort(&savedPort);
   theWindow = FrontWindow();
   while (theWindow!= nil) {
      if (IsAppWindow(theWindow)) {
         SetPort(theWindow);
         if(gTerm != nil)
            TMResume(gTerm, becomingActive);
         if(gConn != nil)
            CMResume(gConn, becomingActive);
         if(gFT != nil)
            FTResume(gFT, becomingActive);
      }
      theWindow = (WindowPtr)(((WindowPeek) theWindow)->nextWindow);
   }
   SetPort(savedPort);
}
#pragma segment Main
/* (de)activates window */
void DoActivate(window, becomingActive)
   WindowPtr   window;
   Boolean       becomingActive;
{
   if (IsAppWindow(window)) {   // does window belong to us?
      SetPort(window);   // set current port
      if(gConn != nil)   // do we have a valid connection?
         CMActivate(gConn, becomingActive);// activate it
      if(gTerm != nil)   // do we have a terminal?
         TMActivate(gTerm, becomingActive); // activate it
      if(gFT != nil)      // do we have a valid file transfer?
         FTActivate(gFT, becomingActive);// activate it
   }
}
#pragma segment Main
/* tries to pass event to a tool if window is a tool window;
                                           handles event if appropriate */
Boolean DoToolEvent(event, window)
   EventRecord   *event;
   WindowPtr   window;
{
   Boolean    doToolEvent;
   if (window != nil && !IsAppWindow(window)) { //   is window valid?
      doToolEvent = true;
      /* copies of commtb record must be in refCon field of
         window for changing the settings   */
          if(gFT != nil && gFT == (FTHandle)GetWRefCon(window))
           FTEvent(gFT,event); // handle file transfer manager event
          else if(gConn != nil && gConn == (ConnHandle)GetWRefCon(window))
           CMEvent(gConn,event); // handle connection manager event
          else if(gTerm != nil && gTerm == (TermHandle)GetWRefCon(window))
           TMEvent(gTerm,event); // handle terminal manager event
          else
         doToolEvent = false;
   }
   else
      doToolEvent = false;
   return(doToolEvent);
}
#pragma segment Main
/* idles all communications tools; this was taken from the Surfer pascal
   example provided from Apple -- you can get it from APDA.*/
void DoIdle()
{
   WindowPtr   theWindow;
   Boolean    doFT, doTM;
   GrafPtr    savedPort;
   GetPort(&savedPort);
   theWindow = FrontWindow();
   while (theWindow != nil) {
      if (IsAppWindow(theWindow)) {
         SetPort(theWindow);
         //TEIdle(gTE);
         if(gConn != nil)
            CMIdle(gConn);
         doFT = false;
         doTM = true;
         if (gFT != nil ) {
            if (((**gFT).flags  & ftIsFTMode) != 0) {
               doFT = true;
               gWasFT = true;
                             if(((**gFT).attributes & ftSameCircuit) != 0)
                  doTM = false;
            }
            else {
               if (gWasFT) {
                  gWasFT = false;
                     if(((**gFT).flags & ftSucc) == 0)
                     ;
                  AddFTSearch();
               }
               if(gStartFT)
                  DoReceive();
            }
            if (doFT)
               FTExec(gFT);
         } /* if gFT != nil  */
         if(gTerm != nil) {
            if (doTM) {
               TMIdle(gTerm);
               TermRecvProc();
            }
         }/* gTerm != nil */
      }
      theWindow = (WindowPtr)(((WindowPeek)theWindow)->nextWindow);
   }
   SetPort(savedPort);
}












Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.