home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
source
/
telex1.sit
/
Telexƒ
/
TelexDA.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-05-04
|
36KB
|
907 lines
/* ====================================== *\
** == AppleTalk Teletype == **
** == Client DA (Message Sender) == **
** == Copyright ⌐ Gamma Software, 1990 == **
** == Think C version == **
\* ====================================== */
/* == standard definitions === */
typedef int boolean;
typedef unsigned char unchar;
#define NIL 0
#define FALSE 0
#define TRUE 1
#define ETX 0x03
#define ScrSize 16
#include <AppleTalk.h>
#include <nAppleTalk.h>
/*
* AppleTalk standard definitions
*/
#define HomeID 0xC120 /* Chooser("System") Name */
#define zipSocket 6 /* ZIP socket number in Router */
#define ATtype "\pTelex" /* Telex Listener AT type */
/*
* AppleTalk IO parameters
*/
/* SendRequest (i.e. send message) */
#define ReqTimeOut 2 /* Timeout for MSG SendRequest */
#define ReqRepeat 3 /* Number of rep while sending */
/* NBPLookup (i.e. look for listeners in the zone) */
#define MaxToGet 50 /* Max Number of Listeners */
#define AskTimeOut (5*8) /* Time-out for NBP-operations */
#define AskPolling (30*60) /* Max time to wait before drop */
/* GetZoneList (i.e. look for all zones names) */
#define ZipTimeOut (5*8) /* Time-out for GetZonesList */
#define ZipRepeat 1 /* Number of retries for -- " -- */
#define ZipPolling (120*60) /* Period of GetZoneList polling */
#define dialogID 0 /* rsrc ID (owned): DA DLOG */
#define aboutID 1 /* rsrc ID (owned): About Alert */
/*
* Codes of Error Messages:
* O.K., Listener is died, IO error, Listener buffer is full, System Error
*/
enum {SendOK=0, SendNoAb, SendIOerr, SendAbFull, SendSysErr};
/*
* Items in the Telex DA dialog box
*/
enum {itSend = 1, itMessage, itList, itAbout, itError, itZones};
short theResID; /* DA rsrc ID (add to owned IDs) */
DialogPtr theDialog = NIL; /* DA dialog */
ControlHandle sendButton; /* "Send" Button, obviously */
boolean modNames; /* namesList selection modified */
boolean modZones; /* zonesList selection modified */
ListHandle namesList; /* listeners list: names and addr*/
ListHandle zonesList; /* zones list: names and nums*/
short theBridge;
MPPParamBlock askRecord; /* ParameterBlock for NPBLookup */
ATPParamBlock zipRecord; /* ParameterBlock for ZIPGetZone */
short zipIndex; /* index for GetZones serial */
long zipTimer; /* latest time GetZones called */
unchar userName[33] = "\pHacker"; /* UserName (it's sent with msgs)*/
unchar zoneName[33] = "\p*"; /* the current zone name */
unchar myEntity[1+32+1+32+1+32]; /* entity for NPBLookup requests */
unchar namesBuffer[MaxToGet*40]; /* buffer for NBPLookup results */
unchar zonesBuffer[600]; /* buffer for ZIPlist results */
short namesProcessed; /* NBPLookup results processed */
Rect namesRect,zonesRect; /* Lists rectangles in the window*/
static BDSElement zipBDS = {0,NIL,0,0}; /* response descr for ZIP calls */
/* == Driver routines (called from Think C DA header) == */
short DAOpen (CntrlParam *ctlPB, DCtlPtr dCtl);
short DAClose (CntrlParam *ctlPB, DCtlPtr dCtl);
short DAControl(CntrlParam *ctlPB, DCtlPtr dCtl);
/* == Main module routines == */
static void doCtlEvent (EventRecord*); /* process a DA event */
static void doEdit (short); /* process the Edit Menu commands*/
static void doSend (void); /* send a message */
static void SayError(short); /* Draw an error message */
static pascal void ProcList(DialogPtr,short);/* Draw Names and Zones Lists */
static void UpdateLists(void); /* Process Names and Zones Lists */
static void SendMessage(Str255); /* Send a message to all selected*/
/* === NBPLookup procedures === */
static short StartLookup(void);
static void KillLookup(void);
static void FillNames (void);
/* === ZIP GetZoneList procedures === */
static void StartZoneList(void);
static void KillZoneList(void);
static void FillZones (void);
/* === Lists (Names & Zones) procedures == */
static short PutList(ListHandle,unchar*,short);
static short GetList(ListHandle,unchar*);
static void ClearList(ListHandle,long);
/*
* This is the main module called from the Think C DA header
*/
main(CntrlParam *ctlPB, DCtlPtr dCtl, short n) {
short retcode = noErr;
GrafPtr savePort;
GetPort(&savePort); /* save the current GrafPort */
switch(n) {
case 0: retcode=DAOpen (ctlPB,dCtl); break;
case 2: retcode=DAControl(ctlPB,dCtl); break;
case 4: retcode=DAClose (ctlPB,dCtl); break;
}
SetPort(savePort); /* restore the current GrafPort */
return(retcode);
}
/*
* Open the DA: create the window, lists, etc; start pollings
*/
short DAOpen(CntrlParam *ctlPB, DCtlPtr dCtl) {
Rect listRect,sendBox;
Rect empty;
Point cSize;
short itemType,refNum,retcode;
Handle item;
FontInfo theInfo;
short namesProcessed; /* NBPLookup results processed */
dCtl->dCtlFlags |= /* This DA should be locked, it */
dNeedLock | dNeedTime | dNeedGoodBye; /* should be called periodically */
/*
* If the windowPtr is non-nil, we already have the window opened.
* This desk accessory ignores multiple opens.
*/
if(dCtl->dCtlWindow != NIL) return(noErr);
if((retcode = OpenDriver("\p.MPP",&refNum)) != noErr) {
SysBeep(1); return(retcode); /* an attempt to open MPP failed */
}
theBridge = (unchar)GetBridgeAddress(); /* where is an internet router? */
/*
* Think C allocates the pool for the "Globals" itself;
* We should only check that it hasn't failed
*/
if(dCtl->dCtlStorage==NIL) return(memFullErr); /* "Globals" allocation failed*/
theResID = 0xC000 |((~dCtl->dCtlRefNum)<<5); /*calculate our DA resource ID */
/*
* Create our Dialog window: Get a template from the DA resource file
*/
theDialog = GetNewDialog(theResID+dialogID,NIL,(WindowPtr)-1);
if(theDialog == NIL) return(resNotFound); /* dialog window creation failed*/
/*
* Set windowKind to the DA refNum, which is negative.
*/
((DialogPeek)theDialog)->window.windowKind = dCtl->dCtlRefNum;
/*
* Store "our globals" address into refCon to use it from the UserItem processor
*/
((DialogPeek)theDialog)->window.refCon = (long)dCtl->dCtlStorage;
/*
* Store the windowPtr in the Device Control Entry
*/
dCtl->dCtlWindow = (WindowPtr)theDialog;
/*
* Change Dialog Font (I don't know any other way to do it)
* This DA uses appFont for its Dialog window
*/
SetPort(theDialog);
TextFont((*((DialogPeek)theDialog)->textH)->txFont = applFont);
TextSize((*((DialogPeek)theDialog)->textH)->txSize = 10);
TextFace(0);
/*
* Calculate the vertical size of Lists cells
*/
GetFontInfo(&theInfo);
cSize.v = theInfo.ascent+theInfo.descent+theInfo.leading;
/*
* Customize Dialog items: define DrawProc for UserItems (they are Lists)
*/
/* process the List "Listeners Names" */
GetDItem(theDialog,itList,&itemType,&item,&listRect);
SetDItem(theDialog,itList, userItem,(Handle)ProcList,&listRect);
listRect.right -= ScrSize-1;
namesRect = zonesRect = listRect;
if(theBridge != 0) { /* we have a bridge -> */
short listV = (listRect.bottom-listRect.top-1)/2/cSize.v*cSize.v;
namesRect.bottom = namesRect.top +listV; /* calculate sizes of 2 Lists: */
zonesRect.top = zonesRect.bottom-listV; /* Names List and Zones List */
} else { /* no bridge found - only 1 List*/
namesRect.bottom = namesRect.top + (listRect.bottom-listRect.top)/cSize.v*cSize.v;
}
/* Now create the Names List */
empty.top = empty.left = empty.bottom = cSize.v = 0;
cSize.h = listRect.right - listRect.left;
empty.right = 3; /* the Names list has 3 columns */
namesList = LNew(&namesRect,&empty,cSize,0,theDialog,TRUE,FALSE,FALSE,TRUE);
if(namesList == NIL) return(resNotFound); /* List creation failed */
/* Now create the Zones List (if necessary) */
zonesList = NIL;
if(theBridge != 0) { /* there is a bridge here -> */
Cell locCell = {0,0};
empty.right = 2; /* the Zones List has 2 columns */
zonesList = LNew(&zonesRect,&empty,cSize,0,theDialog,TRUE,FALSE,FALSE,TRUE);
if(zonesList == NIL) return(resNotFound); /* Zones List creation failed */
(*zonesList)->listFlags = lOnlyOne; /* Only One zone can be selected*/
PutList(zonesList,(unchar*)"*",1); /* Local Zone symbol as the 1st */
LSetSelect(TRUE,locCell,zonesList); /* element and then select it */
} else { /* no bridges -> no zones -> */
HideDItem(theDialog,itZones); /* Hide "Zones" StaticText item */
}
namesRect.right += ScrSize-1; /* Add space for scroll bars */
zonesRect.right += ScrSize-1;
/*
* Customize Dialog items: disable "Send" Button
*/
GetDItem(theDialog,itSend,&itemType,(Handle*)&sendButton,&sendBox);
HiliteControl(sendButton,0xFF);
/*
* Customize Dialog items: Select predefined "Message" text
*/
SelIText(theDialog,itMessage,0,10000);
/*
* Build LookupName: "=:Telex", i.e. all names of type "Telex"
*/
{unchar* pName = myEntity;
unchar* pType = (unchar*)ATtype;
short pLeng = *pType;
*pName++ = 1; *pName++ = '=';
while(pLeng-->=0) *pName++ = *pType++;
}
/*
* Build User Name
*/
{ StringHandle sysName = GetString(HomeID);
if(sysName != NIL) { /* OK, "Chooser name" found */
short n = 0; /* -> move it into userName buff*/
while(++n <= **sysName && n<=32) userName[n]=(*sysName)[n];
*userName = n-1;
}
}
/* Bring DA window to front */
ShowWindow(theDialog); SelectWindow(theDialog);
zipRecord.ioResult = askRecord.ioResult = 0; /* emulate successful end of ops*/
if( StartLookup() != noErr) { /* the 1st call of Lookup failed*/
askRecord.ioResult = 1; /* we won't call Lookup any more*/
SayError(SendIOerr); /* show errMessage on screen */
}
modNames = TRUE; zipIndex = 0; zipTimer = 0L;
dCtl->dCtlDelay = 3; /* call us in 3/60 sec. */
return(noErr);
}
/*
* Close the Telex Desk Accessory
*/
short DAClose(CntrlParam *ctlPB, DCtlPtr dCtl) {
if (dCtl->dCtlWindow != NIL) { /* there is the DA window opened*/
KillLookup(); /* kill pending NBP Lookup call */
if(zonesList != NIL) KillZoneList(); /* --- " --- GetZones --"-- */
/* Dispose the Names and Zones Lists */
if(zonesList != NIL) {LDispose(zonesList); zonesList = NIL;}
if(namesList != NIL) {LDispose(namesList); namesList = NIL;}
/* Dispose the DA window */
DisposDialog(theDialog);
dCtl->dCtlWindow = NIL;
}
return(0);
}
/*
* Process DA events
*/
short DAControl(CntrlParam *ctlPB, DCtlPtr dCtl) {
EventRecord nullRecord;
EventRecord* theEvent;
SetPort(theDialog);
switch (ctlPB->csCode) { /* What's up ? */
case accCursor: case accEvent: /* an Apple Event takes place */
InitCursor(); /*????*/
theEvent = *((EventRecord**) &ctlPB->csParam[0]);
doCtlEvent(theEvent); /* process the event */
UpdateLists(); /* update Names and Zones Lists */
break; case accRun: /* DA is called after a delay */
nullRecord.what = nullEvent; /* process as a null event */
UpdateLists(); /* Update both Lists */
dCtl->dCtlDelay = 30; /* O.K. next call - in 1/2 sec */
break; case accUndo: case accCut: case accCopy:
case accPaste: case accClear: /* Edit Menu commands: */
doEdit(ctlPB->csCode); /* process them */
break; case goodBye: /* the heap is about to die */
DAClose(ctlPB,dCtl); /* perform the Close sequence */
}
return(0);
}
/*
* Process Events in the DA
*/
static void doCtlEvent(EventRecord* theEvent) {
DialogPtr dummy;
short itemHit;
Handle theItem;
Rect theBox;
short theType;
/* tune the procedure pointer - DA is floating in RAM (maybe?) */
GetDItem(theDialog,itList,&theType,&theItem, &theBox);
SetDItem(theDialog,itList,userItem,(Handle)ProcList,&theBox);
/* process Command-keys */
if((theEvent->what == keyDown || theEvent->what==autoKey) &&
(theEvent->modifiers & cmdKey)) { /* Is it an Edit menu command */
switch((char)theEvent->message) { /* we have to do it ourselves */
case 'X': case 'x': doEdit(accCut); break;/* not a nice solution, I agree */
case 'C': case 'c': doEdit(accCopy); break;/* but it's the simplest one */
case 'V': case 'v': doEdit(accPaste);break;
default: SysBeep(1);
}
/* process ETX-key */
} else if((theEvent->what == keyDown || theEvent->what==autoKey) &&
(char)theEvent->message == ETX) { /* ETX (Enter) key means "Send" */
doSend();
} else if(DialogSelect(theEvent,&dummy,&itemHit)) { /* hit on an item ? */
Point where;
switch(itemHit) { /* what item ? */
case itSend: /* "Send" button -> */
doSend(); /* send the message */
break; case itList: /* Click in one of the Lists ? */
where = theEvent->where;
GlobalToLocal(&where);
if(PtInRect(where,&namesRect)) { /* Click in the Names List ? */
if(LClick(where,theEvent->modifiers,namesList)) { /* Process it */
doSend(); /* Double-Click:send the message*/
}
modNames = TRUE; /* the Names(selection) modified*/
} else if(zonesList != NIL) { /* Click in the Zones List */
LClick(where,theEvent->modifiers,zonesList); /* a new Zone selected */
modZones = TRUE; /* ZonesList(selection) modified*/
}
break; case itAbout: /* Click in the "About" button */
Alert(theResID+aboutID,NIL); /* show our TradeMark, etc. */
}
}
if(theEvent->what == activateEvt) { /* Activate: (de)activate Lists*/
LActivate(theEvent->modifiers & activeFlag,namesList);
if(zonesList != NIL) LActivate(theEvent->modifiers & activeFlag,zonesList);
}
}
/*
* Execution of the Edit Menu commands(Cut,Copy,Paste,Clear)
*/
static void doEdit(comm) short comm; {
if(comm == accPaste && TEFromScrap() != noErr) SysBeep(1);
switch(comm) {
case accUndo: SysBeep(1); break;
case accCut: DlgCut(theDialog); break;
case accCopy: DlgCopy(theDialog); break;
case accPaste: DlgPaste(theDialog); break;
case accClear: DlgDelete(theDialog);
}
if(comm == accCopy || comm == accCut) {
ZeroScrap(); TEToScrap();
}
}
/*
* Show Message in the itError field
* the procedure gets the String from the STR resource and
* puts it into the StaticText item in the DA dialog window
*/
void SayError(short code) {
Handle theItem; /* staticText item attributes */
short theType;
Rect theBox;
char* perror; /* error message string */
StringHandle theText = NIL; /* error message resource */
/* get the Handle to the StaticText item */
GetDItem(theDialog,itError,&theType,&theItem,&theBox);
if(code == 0) { /* code = 0 -> O.K. -> empty */
perror = "\p";
} else if ((theText=GetString(theResID+code)) != NIL) {
HLock((Handle)theText); /* string is found in STR rsrcs*/
perror = (char*) *theText;
} else { /* STR resource is absent ?! */
perror = "\pError"; /* -> use the hard-coded string*/
}
SetIText(theItem,perror); /* string -> StaticText item */
if(theText != NIL) HUnlock((Handle)theText); /* Unlock the STR resource */
SetPort(theDialog); InvalRect(&theBox); /* force item updating */
if(code != 0) SysBeep(1); /* error -> Bee-beep! */
}
/*
* Update UserItem: Names and (optionally) Zones Lists
*/
static pascal void ProcList(DialogPtr myDialog, short itemNo) {
Rect xRect;
Ptr savedA4 = /* this is our "Globals" addr. */
*(Handle)((DialogPeek)myDialog)->window.refCon;
asm { /* set A4 to use our "globals" */
MOVE.L savedA4,D0 /* D0 = "our A4" */
MOVE.L A4,savedA4 /* current A4 -> savedA4 */
MOVE.L D0,A4 /* "our A4" -> A4 */
} /* now we can use our Globals */
SetPort(theDialog); /* start drawing Lists */
xRect = namesRect; InsetRect(&xRect,-1,-1); FrameRect(&xRect);
LUpdate(theDialog->visRgn,namesList); /* The Names List is drawn */
if(zonesList != NIL) { /* if the Zones List is present*/
xRect = zonesRect; InsetRect(&xRect,-1,-1); FrameRect(&xRect);
LUpdate(theDialog->visRgn,zonesList); /* the Zones List is drawn */
}
asm { /* Thnx, A4! Now we restore you*/
MOVE.L savedA4,A4
}
}
/* ==================== DA Lists Processing Routines ========================= */
/*
* Update Names and Zones Lists
* 1. Update the Zones List (if any)
* if the selected Zone has been changed:
* kill pending NBPLookup;
* clear the Names List;
* restart NBPlookup in the new Zone
* 2. Update the Names List
* if there are selected Names, Highlight the "Send" button
*/
static void UpdateLists() {
if(zonesList != NIL) { /* we have the Zones List */
FillZones(); /* fill New Zones, drop Died */
if(modZones) { /* the Zone has been changed ? */
Str32 newZone;
short lName =0;
GetList(zonesList,newZone); /* get the name of the new Zone*/
if(*newZone == 0) { /* no Zone is selected ? */
Cell locCell = {0,0}; /* Select "*" (local) Zone */
LSetSelect(TRUE,locCell,zonesList);
GetList(zonesList,newZone); /* and get it's name (i.e. "*")*/
}
/* compare the selected zone with the old one */
while(lName < *newZone+1 && zoneName[lName] == newZone[lName]) ++lName;
if(lName < *newZone+1) { /* the Zone has been changed */
while(lName< *newZone+1) {zoneName[lName] = newZone[lName]; ++lName;}
LDelRow(0,0,namesList); /* clear old Obj Names */
KillLookup(); /* kill Names Lookup */
askRecord.NBPnumGotten = 0; /* FillNames will restart it */
}
}
}
FillNames(); /* Update the Names List */
if(modNames) { /* The Names List was changed->*/
Cell theCell = {0,0}; /* Hilite the SEND button */
HiliteControl(sendButton, LGetSelect(TRUE,&theCell,namesList) ? 0x00 : 0xFF);
modNames = FALSE;
}
}
/* ================ NBP Lookup operations (the Names List) =================== */
/*
Algorithm:
1. Start NBPLookup in the current selected zone (pZone)
2. when the next Listener (i.e. AT Registered object of type "Telex") is found
a) if absent, insert it into the Names list (with its AT address)
b) set the current time into the List element
3. when the NBPLookup is finished, restart it again
4. When a listener has died (the latest response >AskPolling secs ago),
delete that element from the list
*/
/*
* Create the sample name for Lookup and start NBP Lookup call
*/
static short StartLookup() {
short lZone = *zoneName+1; /* length of the Zone Name */
unchar* pZone = myEntity; /* Ptr to AT object name */
pZone += *pZone+1; pZone += *pZone+1; /* Ptr to AT zone name */
/* put the Zone Name into NBP entity structure */
while(--lZone>=0) pZone[lZone] = zoneName[lZone];
/* now fill all fields for NBPLookup call */
askRecord.ioCompletion = NIL; /* we don't need ioCompletion */
askRecord.MPPcsCode = tNBPLookup;
askRecord.NBPinterval = AskTimeOut; /* time to wait for responses */
askRecord.NBPcount = 1; /* no repeats - we'll restart all*/
askRecord.NBPentityPtr = (Ptr)myEntity; /* NBP entity to look for */
askRecord.NBPretBuffPtr = (Ptr)namesBuffer; /* Buffer for responses */
askRecord.NBPretBuffSize= sizeof(namesBuffer);
askRecord.NBPmaxToGet = MaxToGet; /* Max number of responses */
namesProcessed = 0; /* number of responses processed */
return(PLookupName(&askRecord,TRUE)); /* start async NBPLookup */
}
/*
* Kill NBPLookup request (if pending).
* The routine is used in Close sequence and if the Zone name is changed
*/
static void KillLookup() {
MPPParamBlock askKill;
if(askRecord.ioResult > 0) { /* the NBPLookup req is pending */
askKill.MPPnKillQEl = (Ptr)&askRecord; /* we try to kill this request */
PKillNBP(&askKill,FALSE);
}
}
/*
* Process NBP Lookup results.
* if new (i.e. not processed yet) responses have appeared in the buffer,
* process them (i.e. insert/update the Names List)
* Note: Listeners in our Mac are ignored
*
* Also check for died Listeners, but not more often than AskPolling/2 secs
*/
static void FillNames() {
static long lastclear = 0;
short retcode;
unsigned short myNode,myNet;
GetNodeAddress(&myNode,&myNet); /* our NodeID, NetID */
while(namesProcessed<askRecord.NBPnumGotten) { /* cycle for all new responses*/
Cell theCell = {0,2};
EntityName theName;
AddrBlock theAddress;
NBPExtract(namesBuffer,askRecord.NBPnumGotten,namesProcessed+1,
&theName,&theAddress); /* extract the buffer data */
if((theAddress.aNet != 0 && theAddress.aNet != myNet) ||
theAddress.aNode != myNode) { /* we ignore Listeners in OUR Mac*/
theCell.v = /* put this name into Names List */
PutList(namesList,theName.objStr+1,*theName.objStr);
LSetCell((char*)&theAddress, /* Set the AT addr. into 2 column*/
sizeof(theAddress),theCell,namesList);
}
++namesProcessed;
}
/* if NBPLookup has finished, restart it again, if any Zone is selected */
if(*zoneName != 0) {
if(askRecord.ioResult <=0 && (retcode = StartLookup()) != 0) {
SysBeep(1); /* restart failed: Bee-beep!! */
}
}
/* now clear names that are too old */
if(TickCount()-lastclear >= AskPolling/2) { /*It's the time to check for died*/
lastclear = TickCount(); /* remember the current time */
ClearList(namesList,lastclear-AskPolling);/* drop anybody who is too old */
modNames = TRUE; /* Names List could be changed */
}
}
/* ================ ZIP GetZone operations (the Zones List) ================== */
static void StartZoneList() {
static struct {unchar cmd,dummy; short index;} theUserBytes = {8,0,0};
zipBDS.buffSize = sizeof(zonesBuffer); /* prepare data for the ZIP call */
zipBDS.buffPtr = (Ptr)zonesBuffer;
theUserBytes.index = zipIndex; /* the Index in the router tables*/
/* Fill all fields for ATP request to ZIP socket of theBridge */
zipRecord.ATPioCompletion = NIL;
zipRecord.ATPreqPointer = NIL; /* no data for ZIP GetZones req */
zipRecord.ATPreqLength = 0; /* everything is in UserBytes */
zipRecord.ATPuserData = *(long*)&theUserBytes;
zipRecord.ATPcsCode = tATPSndRequest;
zipRecord.ATPatpFlags = 0;
zipRecord.ATPaddrBlock.aNode = theBridge; /* we send ZIP requests to the */
zipRecord.ATPaddrBlock.aNet = 0; /* zipSocket (6) in theBridge */
zipRecord.ATPaddrBlock.aSocket = zipSocket;
zipRecord.ATPnumOfBuffs = 1; /* we have 1 buffer for responses*/
zipRecord.ATPbdsPointer = (Ptr) &zipBDS;
zipRecord.ATPtimeOutVal = ZipTimeOut;
zipRecord.ATPretryCount = ZipRepeat;
if(PSendRequest(&zipRecord,TRUE) != noErr){ /* ZIP GetZoneNames start failed?*/
SysBeep(1); /* failed -> Bee-beep! */
zipBDS.userBytes = 0L; /* emulate the end of responses */
}
}
/*
* Kill pending GetZoneNames ZIP request
* It is used in CloseDA sequence
*/
static void KillZoneList() {
ATPParamBlock zipKill;
if(zipRecord.ioResult > 0) { /* the ZIP request is pending */
zipKill.ATPaKillQEl = (Ptr)&zipRecord; /* kill it as an ATP request */
PKillSendReq(&zipKill,FALSE);
}
}
/*
* Fill the Zones List
* if the current request isn't completed yet, do nothing;
* if there is no request in progress and
* it is the time to refresh the Zones List => Start the ZIP requests serial
* if a request is completed:
* for all Zone Names read:
* insert it into the List, if it is a new one;
* store the current time to the column #1 of the List
* start the next request, if this one isn't the last in the serial
* if the response is the last, drop all zones that were missed in this poll
*/
static void FillZones() {
static long firstTime;
if(zipRecord.ioResult>0) return; /* a request isn't completed yet */
if(zipIndex == 0) { /* there is no pending request ->*/
if(TickCount()-zipTimer >= ZipPolling) { /* if it's time to start polling */
zipTimer = TickCount(); /* reset zipIndex to restart it */
zipIndex = 1; /* at the next call */
PutList(zonesList,(unchar*)"*",1); /* put Local Zone symbol */
}
} else if(zipRecord.ioResult == 0) { /* a response received */
short nZones = (short) (zipBDS.userBytes & 0xFFFF);
short xZones = 0;
unchar* pNames = zonesBuffer;
if(zipIndex == 1) { /* this is the first response */
firstTime = TickCount(); /* remember the current time */
}
while(xZones++ < nZones) { /* put Zones names into ZonesList*/
PutList(zonesList,pNames+1,*pNames);
pNames += *pNames+1;
}
if(zipBDS.userBytes & 0xFF000000) { /* this is the last response -> */
ClearList(zonesList,firstTime); /* drop died zones from the List */
zipIndex = 0; modZones = TRUE;
} else { /* we must continue to read all */
zipIndex += nZones; /* zones from the router */
}
} else { /* ZIP request failed */
SysBeep(1);
}
if(zipIndex != 0) StartZoneList(); /* continue (or repeat, if error)*/
}
/* ========================= List Procedures =============================== */
/*
* Set data into the List, if it is absent (in the column #0)
* Set current time to the data row (into the column #1)
*/
static short PutList(ListHandle theList, unchar* data, short size) {
Cell theCell = {0,0};
boolean found;
long ticks = TickCount();
while((found = LSearch((Ptr)data,size,NIL,&theCell,theList)) && theCell.h != 0) {
theCell.h=0; ++theCell.v; /* only column #0 is needed */
}
if(!found) { /* there is a new entity */
theCell.v = (*theList)->dataBounds.bottom; theCell.h = 0;
LAddRow(1,theCell.v,theList); /* insert the new element */
LSetCell((Ptr)data,size,theCell,theList); /* put the data into the 0-column*/
if(theList==namesList && theCell.v == 0) {/* the first elem in the Names ->*/
LSetSelect(TRUE,theCell,theList); /* automatically select it */
modNames = TRUE; /* Names selection changed! */
}
LDraw(theCell,theList); /* draw the new cell */
}
theCell.h = 1; /* Current Time -> the 1-column */
LSetCell((char*)&ticks,sizeof(ticks),theCell,theList);
return(theCell.v); /* return the element number */
}
/*
* Get the selected data (<= 32 bytes) form theList
* returns the number of the first selected element
* if no element is selected, returns -1
*/
static short GetList(ListHandle theList, unchar* data) {
Cell theCell = {0,0};
if(LGetSelect(TRUE,&theCell,theList)) { /* is there a selected element? */
short length = 32;
LGetCell(data+1,&length,theCell,theList); /* Get the data from the 0-column*/
*data = (unchar)length; /* set the data length */
} else { /* no element is selected -> */
theCell.v = -1; *data=0; /* return -1 and empty data */
}
return(theCell.v);
}
/*
* Clear theList elements older than limit
*/
static void ClearList(ListHandle theList, long limit) {
Cell theCell = {0,1};
while(theCell.v<(*theList)->dataBounds.bottom) {
long theTime; /* for all rows in theList */
short size = sizeof(theTime);
LGetCell(&theTime,&size,theCell,theList); /* get the time from the column#1*/
if(theTime<limit) LDelRow(1,theCell.v,theList); /* delete the row, if older*/
else ++theCell.v; /* otherwise look at the next one*/
}
}
/* ============== Send Messages to the selected Listeners ==================== */
/*
* Send the Message
* Called: "Send" button or "Enter" key or Double-click in the Names List
*/
static void doSend() {
Handle theItem; /* Message EditableText attrs */
Rect theBox;
short theType;
Str255 theMessage; /* Message text */
unchar* pname; /* pointer to the User Name */
int i;
/* Get the Message Text */
GetDItem(theDialog,itMessage,&theType,&theItem,&theBox);
GetIText(theItem,theMessage); /* get the text from the Dialog */
/* Merge with the User Name (if the total length < 0xFF)*/
pname = userName;
if((unsigned short) *theMessage + *pname >= 250) *theMessage = 250 - *pname;
for(i = *pname+1;--i>=0;) theMessage[(unchar)*theMessage+1+i]=pname[i];
SendMessage(theMessage); /* send this data to all selected*/
SelIText(theDialog,itMessage,0,10000); /* Select the text sent */
}
/*
* Send the formed message data to all selected Listeners
*/
static void SendMessage(Str255 theMessage) {
Cell theCell;
int retcode;
short mySocket = 0; /* the socket for transactions */
AddrBlock ForAll = {0,0,0}; /* the socket Mask: no Mask */
short theResp; /* Listener resp. buffer: errCode*/
BDSType BDSArray; /* data for a Listener response */
BDSArray[0].buffSize = sizeof(theResp); /* fill the response */
BDSArray[0].buffPtr = (Ptr) &theResp; /* buffer descriptor */
/*
* Open a Socket for transactions with Listeners
*/
if(ATPOpenSocket(ForAll,&mySocket) != 0) { /* open a socket to send messages*/
SayError(SendSysErr); /* if it failed, inform the user */
return;
}
/*
* Start of the Send-cycle
*/
SetCursor(*GetCursor(watchCursor)); /*watch cursor: we do sync-calls */
SayError(0); /* Clear the Error Message item */
theCell.h = theCell.v = 0;
while(LGetSelect(TRUE,&theCell,namesList)){ /* For all selected Names */
ATPParamBlock DialBlock;
AddrBlock theAddress;
short dataLen = sizeof(theAddress);
/* Get the Listener socket address */
theCell.h = 2; /* the listener AT addr is in the*/
LGetCell((char*)&theAddress,&dataLen, /* column #2 of the Names List */
theCell,namesList);
theCell.h = 0;
/*
* Form the CB for "SendRequest" (Sync)
*/
DialBlock.ATPioCompletion= NIL;
DialBlock.ATPcsCode = tATPSndRequest;
DialBlock.ATPtimeOutVal = ReqTimeOut;
DialBlock.ATPretryCount = ReqRepeat;
DialBlock.ATPuserData = 0;
DialBlock.ATPatpFlags = atpXOvalue;
DialBlock.ATPatpSocket = mySocket; /* our transaction socket */
DialBlock.ATPaddrBlock = theAddress; /* Listener AT address */
DialBlock.ATPreqPointer = (char*)theMessage;
DialBlock.ATPreqLength = *theMessage+1+ /* Message Text + UserName */
theMessage[*theMessage+1]+1;
DialBlock.ATPnumOfBuffs = 1; /* 1 buffer for response(errCode)*/
DialBlock.ATPbdsPointer = (Ptr) BDSArray;/* response buffer: RespWord */
if( (retcode = PNSendRequest(&DialBlock,TRUE)) == noErr) {
EventRecord myRecord; /* while the sending is pending */
while((retcode = DialBlock.ATPioResult) >0) {
GetNextEvent(0,&myRecord); /* almost the same as "sync" call*/
}
}
if(retcode != 0) { /* transaction failed */
SayError(SendIOerr);
} else if (theResp != 0) { /* Listener's buffer is full */
SayError(SendAbFull);
} else { /* O.K. -> deselect the cell */
LSetSelect(FALSE,theCell,namesList);
modNames = TRUE;
}
++theCell.v;
}
/*
* End of the Send-cycle
*/
ATPCloseSocket(mySocket);
InitCursor();
}