// Copyright © 1994 Apple Computer, Inc. All rights reserved constant kAppSymbol := '|LlamaTalk:PIEDTS|; constant kAppName := "Llama Talk 1.0a7"; constant kMaxAppWidth := 240; // original MP width constant kMaxAppHeight := 336; // original MP height // ---- End Project Data ---- // ---- File LlamaTalkMain.t ---- // Before Script for "vDali" // This file and project is Copyright © 1994 Apple Computer, Inc. All rights reserved. vDali := {viewFormat: 83951953, viewBounds: {left: 2, top: 2, right: 242, bottom: 338}, title: "", viewSetupFormScript: func() begin // make view no bigger than the original MP local b := GetAppParams() ; viewBounds := RelBounds( b.appAreaLeft, b.appAreaTop, MIN( b.appAreaWidth, kMaxAppWidth ), MIN( b.appAreaHeight, kMaxAppHeight )); end, viewSetupDoneScript: func() begin :DoUpdateButtons(); end, DoConnect: func() begin :DoMessage("ConnectingÉ"); if not vLlamaTalk:MIsOpen() then vLlamaTalk:Open(); if vProtocol.clusterValue = 1 then begin vLlamaTalk:MSetProtocol('ADSP); vLlamaTalk:MSetAddress("Echo Server:EchoLlama@*"); end else begin vLlamaTalk:MSetProtocol('MNP); vLlamaTalk:MSetAddress(nil); end; vLlamaTalk:MSetQuietDisconnect(true); local err := nil; if vAsync.viewValue then err := vLlamaTalk:MConnectAsync() else if not err := vLlamaTalk:MConnect() then :DoMessage("Connected, waiting for disconnectÉ"); if err then begin :DoMessage("Ready for connect..."); vLlamaTalk:Close(); end; :DoUpdateButtons(); end, DoDisconnect: func() begin vLlamaTalk:MDisconnect(); vLlamaTalk:Close(); :DoUpdateButtons(); :DoMessage("Ready for connectÉ"); end, DoMessage: func(message) begin SetValue(vMessage, 'text, Clone(message)); RefreshViews(); end, DoUpdateButtons: func() begin if vLlamaTalk:MIsOpen() then begin if vLlamaTalk:MIsConnected() then SetValue(vConnect, 'text, "Disconnect") else if vLlamaTalk:MIsConnecting() then SetValue(vConnect, 'text, "Stop Connecting") else SetValue(vConnect, 'text, "Connect"); SetValue(vOpenClose, 'text, "Close"); if vLlamaTalk:MIsVisible() then SetValue(vShowHide, 'text, "Hide") else SetValue(vShowHide, 'text, "Show"); vShowHide:Show(); end else begin SetValue(vConnect, 'text, "Connect"); SetValue(vOpenClose, 'text, "Open"); vShowHide:Hide(); end; end, MLlamaTalk_InputScript: func(data) begin local s := ExtractCString(data, 0); // note: ExtractCString is currently undocumented. Some of the :DoMessage(s); // Extract/Stuff routines have special restrictions and workarounds. end // Read the PIEDTS "Utility Functions" Q&A for more information. , MLlamaTalk_StdError: func(messageText, errorNum) begin :DoUpdateButtons(); if errorNum = 0 then local text := messageText else local text := messageText & "\n" & NumberStr(errorNum); :DoMessage(text); GetRoot():Notify(kNotifyAlert, EnsureInternal(kAppName), EnsureInternal(text)); end, fLlamaTalkState: // this is a REQUIRED slot for use with protoLlamaTalk // it MUST have the initial value of NIL when compiled // it orchestrates the connect and disconnect process nil, MLlamaTalk_StateChanged: func() begin :DoUpdateButtons(); end, _proto: protoApp, debug: "vDali" }; // ---- File protoLlamaTalk ---- // Before Script for "LlamaTalk" /* protoLlamaTalk This layout file is copyright © 1994 Apple Computer, Inc. All rights reserved. Modification Status YY/MM/DD Name Comments 94/09/22 Jim Schram Released as 1.0a7 - removed power-off patch code (install "MP110 Power Off Update.pkg" instead) 94/07/07 Jim Schram Released as 1.0a6 - modified to use kViewIsOpenFunc & kLlamaTalkStuffCStringFunc 94/06/21 Jim Schram Released as 1.0a5 - added improved CloseAppleTalk support 94/06/10 Jim Schram Released as 1.0a4 - added dynamic GotoSleep patch code 94/05/13 Jim Schram Released as 1.0a3 - added support for typed transaction data 94/05/04 Jim Schram Released as 1.0a1 94/03/17 Jim Schram Initial Development --------------------------------------------------------------------- The LlamaTalk packet format is as follows: 1 byte packet type 3 bytes packet length (MSB -> LSB order) N bytes packet data Currently only packet type cLTPacketType_Simple (zero) is implemented. Future packet types will include features such as compression and encryption. All other packet types are reserved. */ DefConst('kDebugLlamaTalk, nil); // true = print comms debugging statements on bunwarmers, nil = supress them DefConst('kLlamaTalkState_Disconnected, nil); // ready-to-go (default state) DefConst('kLlamaTalkState_Connect, 1); // preparation for (asynchronous) connect DefConst('kLlamaTalkState_Connecting, 2); // in-process of (asynchronous) connect DefConst('kLlamaTalkState_Connected, 3); // connected (requires disconnect) DefConst('kLlamaTalkState_Disconnecting, 4); // in-process of (asynchronous) disconnect DefConst('kLlamaTalkType_nil, 0); // transaction data item types, as used in :MWriteObject() -- DO NOT RENUMBER! DefConst('kLlamaTalkType_true, 1); DefConst('kLlamaTalkType_char, 2); DefConst('kLlamaTalkType_integer, 3); DefConst('kLlamaTalkType_real, 4); DefConst('kLlamaTalkType_string, 5); DefConst('kLlamaTalkType_binary, 6); DefConst('kLlamaTalkError_IllegalOperation, -666); DefConst('kLlamaTalkError_EndpointInUse, -667); DefConst('kLlamaTalkMessage_EndpointInUse, "Another application seems to be using the communications port."); DefConst('kLlamaTalkNewBinaryFunc, func(size) SetLength(SetClass(Clone(""), 'binary), size)); // Note: StuffCString is currently undocumented. Use it at your own risk. Some of the Extract/Stuff // routines have bugs or special workarounds. A forthcoming item in the "Q&A - Utility Functions" // document will discuss the workarounds. DefConst('kLlamaTalkStuffCStringFunc, func(theObject, theOffset, theString) begin // modifies and returns theObject local len := StrLen(theString); if len < 4 then begin // work-around for StuffCString bug for i:= 0 to len - 1 do StuffChar(theObject, theOffset + i, theString[i]); StuffByte(theObject, theOffset + len, 0); end else StuffCString(theObject, theOffset, theString); return theObject; end ); DefConst('kLlamaTalkQueueTemplate, // create a new queue like this: kLlamaTalkQueueTemplate:MInstantiate('FIFO); { MInstantiate: func(queueType) // currently 'FIFO (first-in-first-out) and 'FILO (first-in-last-out) queues are supported begin if queueType = 'FIFO or queueType = 'FILO then return { _proto: self, fType: queueType, // 'FIFO or 'FILO fNbElem: 0, // 0 - N fData: [], } // array [] of fNbElem enqueued items else return nil; end, MDispose: func() begin fType := nil; fNbElem := nil; fData := nil; return nil; end, MReset: func() // empties the queue, allowing potential garbage collection to occur begin if fType then begin fNbElem := 0; SetLength(fData,0); end; return nil; end, MEnQueue: func(data) // adds a non-nil data item to a queue according to queue type begin if data then if fType = 'FIFO or fType = 'FILO then begin fNbElem := fNbElem + 1; SetLength(fData, fNbElem); fData[fNbElem - 1] := data; return nil; end; return data; end, MDeQueue: func() // removes a data item from a queue according to queue type begin if not fType or fNbElem <= 0 then return nil; if fType = 'FIFO then begin fNbElem := fNbElem - 1; local data := fData[0]; ArrayMunger(fData, 0, 1, nil, 0, 1); end else if fType = 'FILO then begin fNbElem := fNbElem - 1; local data := fData[fNbElem]; ArrayMunger(fData, fNbElem, 1, nil, fNbElem, 1); end else local data := nil; return data; end, MGetQueueSize: func() // return the number of data items in the queue begin if fType = 'FIFO or fType = 'FILO then return fNbElem else return 0; end, MGetDataSize: func() // return the sum of the binary Length() of each data item in the queue begin local len := 0; if fType = 'FIFO or fType = 'FILO then foreach item in fData do len := len + Length(item); return len; end, }); // the following constants (kDA_StatusFrame, kDA_ActionFunc, kDA_AddDeferredAction) // are used to implement "killable" deferred actions --> see MAddDeferredAction for more info // DefConst( 'kDA_StatusFrame, { fRunIt: true, fRanIt: nil, RanIt: func() fRanIt, KillIt: func() begin fRunIt := nil; fRanIt; end, } ); DefConst( 'kDA_ActionFunc, func(fn, args, status) begin status.fRanIt := true; if status.fRunIt then Apply(fn, args); end ); DefConst( 'kDA_AddDeferredAction, func(fn, args) begin local status := {_proto: kDA_StatusFrame}; AddDeferredAction(kDA_ActionFunc, [fn, args, status]); status; end ); LlamaTalk := { MWriteObject: func(data) // WARNING: This func contains work-arounds and currently undocumented functions. Use at your own risk! begin if fLlamaTalkState <> kLlamaTalkState_Connected then return; IF kDebugLlamaTalk THEN LT_PRINT("Enqueueing data..."); local translation := nil; if not data then begin // Note: if-then-else tree is structured for common-case efficiency translation := :MNewBinary(1); StuffByte(translation, 0, kLlamaTalkType_nil); // object type = nil end else if IsInstance(data, 'string) then begin local len := StrLen(data); translation := :MNewBinary(len + 4); StuffByte(translation, 0, kLlamaTalkType_string); // object type = string StuffByte(translation, 1, len >> 8); // MSB of length StuffByte(translation, 2, len); // LSB of length call kLlamaTalkStuffCStringFunc with (translation, 3, data); // N single byte chars (Mac encoding for chars 128-255) SetLength(translation, len + 3); // get rid of that zero terminator byte from StuffCString end else if IsInstance(data, 'int) then begin translation := :MNewBinary(5); StuffByte(translation, 0, kLlamaTalkType_integer); // object type = integer StuffLong(translation, 1, data); // MSB -> LSB of 4 byte sign-extended value end else if IsInstance(data, 'char) then begin translation := :MNewBinary(2); StuffByte(translation, 0, kLlamaTalkType_char); // object type = char StuffChar(translation, 1, data); // 1 single byte char (Mac encoding for chars 128-255) end else if IsInstance(data, 'boolean) then begin translation := :MNewBinary(1); StuffByte(translation, 0, if data then kLlamaTalkType_true // object type = true else kLlamaTalkType_nil); // object type = nil (= false) end else if IsInstance(data, 'real) then begin local s := NumberStr(data); len := StrLen(s); translation := :MNewBinary((len*2) + 2); // workaround for a bug in StuffPString (overwrites buffer by factor of 2) StuffByte(translation, 0, kLlamaTalkType_real); // object type = real (as an N-char string) StuffPString(translation, 1, s); // pascal string representation (length byte followed by chars) SetLength(translation, len + 2); // reduce the buffer to the correct size end else if IsInstance(data, 'binary) then begin local len := Length(data); translation := :MNewBinary(len + 5); StuffByte(translation, 0, kLlamaTalkType_binary); // object type = binary (bytes) StuffLong(translation, 1, len); // MSB -> LSB of 4 byte sign-extended length of object BinaryMunger(translation, 5, len, data, 0, len); // data bytes end else begin IF kDebugLlamaTalk THEN LT_PRINT("Unsupported data class!!! Cannot enqueue..."); :?MLlamaTalk_StdError("Unsupported data class (" & SPrintObject(ClassOf(data)) & "). Cannot enqueue.", -48215); end; fWriteQueue:MEnQueue(translation); // FYI :MEnQueue() won't enqueue a nil (this is good...) return translation; end, MAddDeferredAction: /* use this method like you would use AddDeferredAction the difference is that this routine returns a frame containing closures which can be used to "cancel" the deferred action before it executes and to query whether or not the deferred action has executed Example: local x := :MAddDeferredAction( func() GetRoot():SysBeep(), [] ); if x:RanIt() then print("already executed"); else print("waiting to run"); if x:KillIt() then print("already executed"); else print("canceled"); obviously you'll keep x around in a slot somewhere if you ever want to cancel the deferred action... */ kDA_AddDeferredAction // (fn, args), viewSetupDoneScript: func() begin AddPowerOffHandler(SELF); // do not allow the Newton to sleep while we're connected end, MIsVisible: func() begin if fBusyShapes then true else nil; end, MRead: func() begin if fInputQueue then fInputQueue:MDeQueue() else nil; end, MIsOpen: func() // this function will only work when 'self' is the protoLlamaTalk view! begin // In other words, we're called using: if view:MIsOpen() then... return call kViewIsOpenFunc with (self); end, viewFormat: nil, MBeginTransaction: func() begin if fLlamaTalkState <> kLlamaTalkState_Connected then return; IF kDebugLlamaTalk THEN LT_PRINT("Beginning write transaction..."); fWriteQueue:MReset(); return true; end, fDisconnectSlip: { _proto: protoFloater, viewBounds: { left: 0, top: 0, right: 108, bottom: 44, }, viewJustify: vjParentCenterH + vjParentCenterV, // 80 powerOffScript: func(what) nil, viewSetupDoneScript: func() AddPowerOffHandler(self), viewQuitScript: func() RemovePowerOffHandler(self), stepChildren: [ { _proto: protoStaticText, viewBounds: { left: 8, top: 8, right: 104, bottom: 40, }, viewJustify: vjCenterH, // 2 text: "Disconnecting...\nPlease Wait...", }, ], }, viewQuitScript: func() begin :MDisconnect(); fInputQueue := fInputQueue:MDispose(); fOutputQueue := fOutputQueue:MDispose(); fWriteQueue := fWriteQueue:MDispose(); fEndPoint := nil; fEndPointAddress := nil; fEndPointConfig := nil; fOutputData := nil; fOutputPhase := nil; fIsQuietDisconnect := nil; fBusyShapes := nil; RemovePowerOffHandler(SELF); // allow the Newton to go to sleep now that we're no longer connected if kDebugLlamaTalk then RemoveSlot(functions, 'LT_PRINT); end, MGetAddress: func() begin if HasSlot(fEndPointAddress, 'data) and HasSlot(fEndPointAddress.data, 'addressData) then return fEndPointAddress.data.addressData else return fEndPointAddress; end, MNewBinary: func(size) begin return call kLlamaTalkNewBinaryFunc with (size); end, viewDrawScript: func() begin if fLlamaTalkState = kLlamaTalkState_Connected then :MDrawBusyIcon('IDLE) else :MDrawBusyIcon('DISCONNECTED); end, FInputHeaderComponent: { inputForm: 'raw, byteCount: 4, inputScript: func(ep, data) begin local size := ExtractByte(data, 1) << 16 + ExtractByte(data, 2) << 8 + ExtractByte(data, 3); IF kDebugLlamaTalk THEN LT_PRINT("FInputHeaderComponent called, byte count =" && NumberStr(size)); if ExtractByte(data, 0) = 0 then begin ep.FInputDataComponent.fData := call kLlamaTalkNewBinaryFunc with (size); ep.FInputDataComponent.fDataIndex := 0; ep.FInputDataComponent.fDataBytesToGo := size; ep.FInputDataComponent.byteCount := if size < 512 then size else 512; ep:SetInputSpec(ep.FInputDataComponent); end else begin ep.FInputDataSkipper.fDataBytesToGo := size; ep.FInputDataSkipper.byteCount := if size < 512 then size else 512; ep:SetInputSpec(ep.FInputDataSkipper); end; end, }, MConnect: func() begin if not :MIsOpen() then // return an error if this view has not yet been opened! return kLlamaTalkError_IllegalOperation; if fLlamaTalkState <> kLlamaTalkState_Disconnected then return kLlamaTalkError_IllegalOperation; :MSetLlamaTalkState(kLlamaTalkState_Connect); fEndPoint.fDeferredObj := nil; return :MConnectAction(fEndPoint); end, fBusyShapes: nil // holds an array of shapes to be drawn during I/O when this view is visible, nil otherwise , MDisconnectCompProc: func(ep, state, slip) // this routine is called as a deferred action from MDisconnect begin if state = kLlamaTalkState_Connected then ep:Disconnect(); ep:Dispose(); ep:MCloseNetStack(); if slip then slip:Close(); ep:MSetLlamaTalkState(kLlamaTalkState_Disconnected); end, viewFlags: 32, fEndPointConfigADSP: { fIODelay: if kDebugLlamaTalk then 7 else 2, // must slow down I/O for ADSP to avoid RPC deadlock bug // bunwarmers need extra slow I/O to handle all the LT_PRINT statements configOptions: [ { label: kCMSAppleTalkID, type: 'service, opCode: opSetRequired }, { label: kCMSAppleTalkID, type: 'option, opCode: opSetRequired, data: kCMOAppleTalkADSP }, { label: kCMOEndpointName, type: 'option, opCode: opSetRequired, data: kADSPEndpoint }, ], }, MIsConnected: func() begin return fLlamaTalkState = kLlamaTalkState_Connected; end, MConnectAsync: func() begin if not :MIsOpen() then // return an error if this view has not yet been opened! return kLlamaTalkError_IllegalOperation; if fLlamaTalkState <> kLlamaTalkState_Disconnected then return kLlamaTalkError_IllegalOperation; :MSetLlamaTalkState(kLlamaTalkState_Connect); fEndPoint.fDeferredObj := :MAddDeferredAction(MConnectAction, [fEndPoint]); return nil; end, viewIdleScript: func() begin if fLlamaTalkState <> kLlamaTalkState_Connected then begin // if not connected, turn off idle handler :Dirty(); return nil; end; if :MGetState() < 3 then begin // if connection has dropped, disconnect and turn off idle handler :MDisconnect(); if not fIsQuietDisconnect then // avoid calling the error method if we're supposed to keep quiet about it :?MLlamaTalk_StdError("The connection has closed unexpectedly.", -1); return nil; end; local outputDone := nil; // this idle script can be turned off when outputDone and no pending input IF kDebugLlamaTalk THEN LT_PRINT("Idle Speed" && NumberStr(fEndPoint.fIdleSpeed)); if fInputQueue:MGetQueueSize() > 0 then begin // if there is any input data available, call input script if defined :DoDrawing('MDrawBusyIcon, ['INPUT]); :?MLlamaTalk_InputScript(:MRead()); end; if fOutputPhase = 0 then // if there is any data to output, start the output state machine if fOutputQueue:MGetQueueSize() > 0 then begin IF kDebugLlamaTalk THEN LT_PRINT("Beginning output phase..."); fOutputData := fOutputQueue:MDeQueue(); // fOutputData = the transaction to be output local len := fOutputData:MGetDataSize(); // len = total bytes of this transaction IF kDebugLlamaTalk THEN LT_PRINT("Total length to be written =" && NumberStr(len)); fEndPoint:Output([0, len >> 16, len >> 8, len], nil); // output the transaction length fEndPoint:FlushOutput(); fOutputPhase := 1; // go to next state (outputting transaction data) end else outputDone := true; else if fOutputPhase = 1 then // output chunks of the transaction until all bytes have been output if fOutputData:MGetQueueSize() > 0 then begin :DoDrawing('MDrawBusyIcon, ['OUTPUT]); local buffer := :MNewBinary(0); // concatenate transaction data items into a single binary object repeat local data := fOutputData:MDeQueue(); if data then begin local dataLen := Length(data); local bufferLen := Length(buffer); SetLength(buffer, bufferLen + dataLen); IF kDebugLlamaTalk THEN LT_PRINT("bufferLen = " & NumberStr(Length(buffer))); BinaryMunger(buffer, bufferLen, dataLen, data, 0, dataLen); end; until Length(buffer) >= 1024 or fOutputData:MGetQueueSize() = 0; if Length(buffer) > 0 then begin // output the transaction data chunk fEndPoint:Output(buffer,nil); fEndPoint:FlushOutput(); end; end else begin fOutputData := nil; // allow garbage collection on this transaction (queue) fOutputPhase := 0; // go to next state end; if outputDone and fInputQueue:MGetQueueSize() = 0 then begin // if no more data to process turn off idle handler :DoDrawing('MDrawBusyIcon, ['IDLE]); return 15000 // slow idle handler (check for dropped connections every 15 seconds) end else return fEndPoint.fIdleSpeed; // number of milliseconds to delay until next idle end, MIsQuietDisconnect: func() begin return fIsQuietDisconnect; end, MSetProtocol: func(protocol) // returns the endpoint configuration frame, or nil if protocol symbol is unsupported or view is not open begin if not :MIsOpen() then return nil; if IsFrame(protocol) then fEndPointConfig := EnsureInternal(protocol) else if protocol = 'MNP then fEndPointConfig := fEndPointConfigMNP else if protocol = 'ADSP then fEndPointConfig := fEndPointConfigADSP else return nil; :MResetConfig(); return fEndPointConfig; end, fEndPointConfig: nil // contains the "live" endpoint config frame used during endpoint instantiation , viewBounds: {left: 100, top: 100, right: 116, bottom: 116}, MCountPendingOutputTransactions: func() begin return if fOutputQueue then fOutputQueue:MGetQueueSize() else 0; end, MEndTransaction: func() begin if fLlamaTalkState <> kLlamaTalkState_Connected then return; IF kDebugLlamaTalk THEN LT_PRINT("Transferring transaction to output queue. Enabling idle handler."); fOutputQueue:MEnQueue(fWriteQueue); fWriteQueue := kLlamaTalkQueueTemplate:MInstantiate('FIFO); :SetUpIdle(100); return nil; end, MDrawBusyIcon: func(state) begin if fBusyShapes then begin :DrawShape(fBusyShapes[0].fShape, fBusyShapes[0].fStyle); // erase the background, then draw the appropriate icon if state = 'DISCONNECTED then :DrawShape(fBusyShapes[1].fShape, fBusyShapes[1].fStyle) else if state = 'IDLE then :DrawShape(fBusyShapes[2].fShape, fBusyShapes[2].fStyle) else if state = 'INPUT then :DrawShape(fBusyShapes[3].fShape, fBusyShapes[3].fStyle) else if state = 'OUTPUT then :DrawShape(fBusyShapes[4].fShape, fBusyShapes[4].fStyle) else :DrawShape(fBusyShapes[1].fShape, fBusyShapes[1].fStyle) end; end, MCountPendingInputTransactions: func() begin return if fInputQueue then fInputQueue:MGetQueueSize() else 0; end, MResetConfig: func() begin fEndPoint.configOptions := fEndPointConfig.configOptions; fEndPoint.fIdleSpeed := (fEndPointConfig.fIODelay + 1) * 100; fEndPoint.MOpenNetStack := func() nil; fEndPoint.MCloseNetStack := func() nil; if fEndPoint.configOptions then foreach option in fEndPoint.configOptions do if option.type and option.type = 'service and option.label and option.label = kCMSAppleTalkID then begin fEndPoint.MOpenNetStack := func() OpenAppleTalk(); fEndPoint.MCloseNetStack := func() CloseAppleTalk(); break; end; end, fOutputPhase: nil // holds phase of output state machine during execution, MResetQueues: func() begin fInputQueue:MReset(); fOutputQueue:MReset(); fWriteQueue:MReset(); end, powerOffScript: func(what) begin if what = 'okToPowerOff and fLlamaTalkState = kLlamaTalkState_Disconnected then TRUE else NIL; end, fEndPointConfigMNP: { fIODelay: 0, // MNP has no RPC deadlock bug, so it can run full-speed comms configOptions: [ { label: kCMSMNPID, type: 'service, opCode: opSetRequired }, { label: kCMOSerialIOParms, type: 'option, opCode: opSetNegotiate, data: { bps: k38400bps, dataBits: k8DataBits, stopBits: k1StopBits, parity: kNoParity } }, { label: kCMOMNPAllocate, type: 'option, opCode: opSetRequired, data: kMNPDoAllocate }, { label: kCMOMNPCompression, type: 'option, opCode: opSetRequired, data: kMNPCompressionNone }, // also works with kMNPCompressionV42bis (but that uses lots more memory) { label: kCMOMNPDataRate, type: 'option, opCode: opSetNegotiate, data: k38400bps }, ], }, MSetAddress: func(address) // returns the address, or nil if view is not open begin if :MIsOpen() then if address then if IsFrame(address) then fEndPointAddress := EnsureInternal(address) else fEndPointAddress := { label: kCMARouteLabel, type: 'address, opCode: opSetRequired, data: { addressType: kNamedAppleTalkAddress, addressData: EnsureInternal(address), }, } else fEndPointAddress := nil else nil; end, MNewQueue: func(queueType) begin return kLlamaTalkQueueTemplate:MInstantiate(queueType); end, MDisconnect: func() // NOTE: this method may also be called from :MEndPointExceptionHandler in which case SELF is the endpoint frame begin if fEndPoint and fEndPoint.fDeferredObj and not fEndPoint.fDeferredObj:KillIt() then :MSetLlamaTalkState(kLlamaTalkState_Disconnected); if fLlamaTalkState <> kLlamaTalkState_Connected and fLlamaTalkState <> kLlamaTalkState_Connecting then return; IF kDebugLlamaTalk THEN LT_PRINT("Disconnecting endpoint"); local currentState := fLlamaTalkState; :MSetLlamaTalkState(kLlamaTalkState_Disconnecting); local slip; if slip := BuildContext(fDisconnectSlip) then slip:Open(); fEndPoint.nextInputSpec := nil; // kill future input fEndPoint:SetInputSpec(nil); // kill current input fEndPoint:Abort(); // kill pending input :MResetQueues(); // blow away all queued data AddDelayedAction(MDisconnectCompProc, [fEndPoint, currentState, slip], 2500); // we must do this as a delayed action! :Dirty(); return nil; end, fEndPoint: nil // contains the "live" endpoint while we're connected, nil otherwise, MSetLlamaTalkState: func(newState) begin local appBaseView := GetRoot().(kAppSymbol); appBaseView.fLlamaTalkState := newState; if call kViewIsOpenFunc with (appBaseView) then begin fLlamaTalk:?MLlamaTalk_StateChanged(); if call kViewIsOpenFunc with (fLlamaTalk) then fLlamaTalk:Dirty(); RefreshViews(); end; end, MAbortPendingTransactions: func() begin fInputQueue:MReset(); fOutputQueue:MReset(); return nil; end, MToggle: func() begin if :MIsOpen() then if :MIsVisible() then :Hide() else :Show(); return nil; end, fOutputData: nil // queue of outgoing transaction data items (transaction is sent in chunks) , MWrite: func(data) begin if fLlamaTalkState <> kLlamaTalkState_Connected then return; IF kDebugLlamaTalk THEN LT_PRINT("Enqueueing data..."); local translation := nil; if IsInstance(data, 'string) then translation := call kLlamaTalkStuffCStringFunc with (:MNewBinary(StrLen(data) + 1), 0, data) else if IsInstance(data, 'binary) then translation := data else begin IF kDebugLlamaTalk THEN LT_PRINT("Unsupported data class!!! Cannot enqueue..."); GetRoot():SysBeep(); end; fWriteQueue:MEnQueue(translation); // Note :MEnQueue() won't enqueue a nil (this is good...) return translation; end, declareSelf: 'fLlamaTalk, viewSetupFormScript: func() begin if kDebugLlamaTalk then functions.LT_PRINT := func(data) // This is VERY dangerous, but since we only do it for a PRIVATE debug build... begin Print(data); // Sleep(10); end; self.fEndPointAddress := TotalClone(fEndPointAddress); // ADSP address frame must be in RAM so we can modify it self.fEndPointConfig := fEndPointConfigADSP; // ADSP is the default configuration for this endpoint proto self.fEndPoint := // the endpoint frame must be in RAM so we can modify it { _proto: protoEndpoint, _parent: self, ExceptionHandler: MEndPointExceptionHandler, FInputHeaderComponent: FInputHeaderComponent, FInputDataComponent: Clone(FInputDataComponent), // shallow clone because top level slots must be modifiable FInputDataSkipper: Clone(FInputDataSkipper), // shallow clone because top level slots must be modifiable configOptions: nil, // these nil slots are defined here to help reduce the number of fIdleSpeed: nil, // frame map entries, but their values are set in :MResetConfig() MOpenNetStack: nil, // bug fix for endpoints that use network protocols MCloseNetStack: nil, // bug fix for endpoints that use network protocols fDeferredObj: nil, // holds a "magic frame" that allows us to cancel deferred actions(!) }; self:MResetConfig(); // initialize the endpoint frame's configuration slots self.fInputQueue := :MNewQueue('FIFO); self.fOutputQueue := :MNewQueue('FIFO); self.fWriteQueue := :MNewQueue('FIFO); self.fOutputPhase := 0; end, FInputDataSkipper: { inputForm: 'raw, byteCount: 0, fDataBytesToGo: 0, inputScript: func(ep, data) begin IF kDebugLlamaTalk THEN LT_PRINT("FInputDataSkipper called..."); fDataBytesToGo := fDataBytesToGo - byteCount; if fDataBytesToGo > 0 then begin byteCount := if fDataBytesToGo < 512 then fDataBytesToGo else 512; ep:SetInputSpec(ep.FInputDataSkipper); end else ep:SetInputSpec(ep.FInputHeaderComponent); end, }, MEndPointExceptionHandler: func(exception) begin IF kDebugLlamaTalk THEN LT_PRINT("MEndPointExceptionHandler called!"); if exception.data exists and exception.data <> -16005 // exception generated by ep:Abort() while connected -- really not an error and exception.data <> -16013 // exception generated by ep:Abort() during connect -- really not an error and fLlamaTalkState <> kLlamaTalkState_Disconnecting then begin if exception.data = -20003 // the other end has torn down its connection or exception.data = -16009 then // wouldn't it be nice if all endpoints used the same error codes? if fIsQuietDisconnect then // avoid calling the error method if we're supposed to keep quiet about it nil else fLlamaTalk:?MLlamaTalk_StdError("The connection has closed unexpectedly.", exception.data) else fLlamaTalk:?MLlamaTalk_StdError("A communications error has occured.", exception.data); AddDeferredAction(func(context) context:MDisconnect(), [fLlamaTalk]); end; return true; end, MSetQuietDisconnect: func(quiet) // returns true or nil begin fIsQuietDisconnect := if quiet then true else nil; end, fEndPointAddress: nil // contains the connection address frame, or nil if not required , fWriteQueue: nil // queue of outgoing transaction data items (entire queue is a transaction) , MIsConnecting: func() begin return fLlamaTalkState = kLlamaTalkState_Connecting; end, MConnectCompProc: // this routine is called from MConnectAction // SELF is the endpoint frame func(ep, err) begin if not err then begin :MSetLlamaTalkState(kLlamaTalkState_Connected); :SetInputSpec(FInputHeaderComponent); // kick-off the receive end; else if err <> -10039 then begin fLlamaTalk:MDisconnect(); IF kDebugLlamaTalk THEN LT_PRINT("ERROR in MConnect Connect, error code =" && NumberStr(err)); fLlamaTalk:?MLlamaTalk_StdError("Could not connect.", err); end; return err; end, FInputDataComponent: { inputForm: 'raw, byteCount: 0, fDataBytesToGo: 0, fDataIndex: 0, fData: nil, inputScript: func(ep, data) begin IF kDebugLlamaTalk THEN LT_PRINT("FInputDataComponent called..."); BinaryMunger(fData, fDataIndex, byteCount, data, 0, byteCount); fDataIndex := fDataIndex + byteCount; fDataBytesToGo := fDataBytesToGo - byteCount; if fDataBytesToGo > 0 then begin byteCount := if fDataBytesToGo < 512 then fDataBytesToGo else 512; ep:SetInputSpec(ep.FInputDataComponent); end else begin ep._parent.fInputQueue:MEnQueue(fData); ep:SetInputSpec(ep.FInputHeaderComponent); ep._parent:SetUpIdle(100); end; end, }, viewHideScript: func() begin fBusyShapes := nil; end, viewclass: 74, fOutputQueue: nil // each entry holds an outgoing transaction (a queue) until it can be processed , MConnectAction: // this routine is called normally from MConnect, but as a deferred action from MConnectAsync // therefore, we cannot depend on the context of SELF, so give everything the ep frame context func(ep) begin local err := kLlamaTalkError_EndpointInUse; ep:MOpenNetStack(); try err := ep:Instantiate(ep, NIL) onexception |evt.ex| do begin if HasSlot(CurrentException(), 'error) then err := CurrentException().error; ep:MCloseNetStack(); IF kDebugLlamaTalk THEN LT_PRINT("ERROR in MConnectAction Instantiate, error code =" && NumberStr(err)); ep:?MLlamaTalk_StdError(kLlamaTalkMessage_EndpointInUse, err); ep:MSetLlamaTalkState(kLlamaTalkState_Disconnected); return; end; ep:MSetLlamaTalkState(kLlamaTalkState_Connecting); err := ep:Connect(ep._parent.fEndPointAddress, nil); ep:MConnectCompProc(ep, err); end, debug: "LlamaTalk", MAbortTransaction: func() begin IF kDebugLlamaTalk THEN LT_PRINT("Aborting current transaction..."); return :MBeginTransaction(); end, fInputQueue: nil // holds incoming data until it can be processed, viewShowScript: func() begin local r := :LocalBox(); local s := MakeOval(0, 0, r.right - 1, r.bottom - 1); fBusyShapes := [ { fShape: [ MakeShape(r) ], // used to erase the view fStyle: { transferMode: modeCopy, penPattern: vfNone, fillPattern: vfFillWhite, } }, { fShape: [ s, // 'DISCONNECTED phase MakeLine(0, 0, r.right - 1, r.bottom - 1), MakeLine(0, r.bottom - 1, r.right - 1, 0) ], fStyle: { transferMode: modeCopy, penSize: 1, penPattern: vfBlack, fillPattern: vfFillWhite, } }, { fShape: [ s ], // 'IDLE phase fStyle: { transferMode: modeCopy, penSize: 1, penPattern: vfBlack, fillPattern: vfFillWhite, } }, { fShape: [ s ], // 'INPUT phase fStyle: { transferMode: modeCopy, penSize: 1, penPattern: vfBlack, fillPattern: vfFillGray, } }, { fShape: [ s ], // 'OUTPUT phase fStyle: { transferMode: modeCopy, penSize: 1, penPattern: vfNone, fillPattern: vfFillBlack, } }, ]; end, MGetState: func() begin local state := -1; if fLlamaTalkState = kLlamaTalkState_Connected then try state := fEndPoint:State(); onexception |evt.ex| do state := kLlamaTalkError_IllegalOperation; return state; end, fIsQuietDisconnect: nil }; // ---- Back in File LlamaTalkMain.t ---- vLlamaTalk := /* child of vDali */ {viewBounds: {left: 8, top: 141, right: 24, bottom: 157}, _proto: LlamaTalk, debug: "vLlamaTalk" }; // View vLlamaTalk is declared to vDali vMessage := /* child of vDali */ {text: "Ready for connect\u2026", viewBounds: {left: 9, top: 25, right: 229, bottom: 125}, viewJustify: 0, viewFormat: 524624, _proto: protoStaticText, debug: "vMessage" }; // View vMessage is declared to vDali vStatus := /* child of vDali */ {text: "", viewBounds: {left: 32, top: 136, right: 224, bottom: 168}, viewIdleScript: func() begin SetValue (vStatus, 'text, "In =" && NumberStr(vLlamaTalk:MCountPendingInputTransactions()) & " " & "Out =" && NumberStr(vLlamaTalk:MCountPendingOutputTransactions()) & "\n" & "State =" && NumberStr(vLlamaTalk:MGetState()) & " " & (if fLlamaTalkState then NumberStr(fLlamaTalkState) else "nil") & " " & "Ticks =" && NumberStr(Ticks()) ); 1000; // 'idle one second': return the number of milliseconds to wait before calling IdelScript again end, viewSetupDoneScript: func() begin :SetUpIdle(100); end, viewJustify: 0, _proto: protoStaticText, debug: "vStatus" }; // View vStatus is declared to vDali vConnect := /* child of vDali */ {text: "", buttonClickScript: func() begin if vLlamaTalk:MIsConnected() then :DoDisconnect() else :DoConnect(); :DoUpdateButtons(); end, viewBounds: {left: 122, top: 210, right: 222, bottom: 230}, _proto: protoTextButton, debug: "vConnect" }; // View vConnect is declared to vDali vOpenClose := /* child of vDali */ {text: "", buttonClickScript: func() begin vLlamaTalk:Toggle(); :DoUpdateButtons(); end, viewBounds: {left: 122, top: 242, right: 166, bottom: 262}, _proto: protoTextButton, debug: "vOpenClose" }; // View vOpenClose is declared to vDali vShowHide := /* child of vDali */ {text: "", buttonClickScript: func() begin vLlamaTalk:MToggle(); :DoUpdateButtons(); end;, viewBounds: {left: 178, top: 242, right: 222, bottom: 262}, viewFlags: 515, _proto: protoTextButton, debug: "vShowHide" }; // View vShowHide is declared to vDali vSendPings := /* child of vDali */ {text: "Start Ping Test", buttonClickScript: func() begin if fIsActiveIdle then begin fIsActiveIdle := nil; SetValue(self, 'text, "Start Ping Test"); :SetUpIdle(0); end else begin fIsActiveIdle := true; SetValue(self, 'text, "Stop Ping Test"); :SetUpIdle(100); end; end, viewBounds: {left: 122, top: 274, right: 222, bottom: 294}, viewIdleScript: func() begin if vLlamaTalk:MCountPendingOutputTransactions() < 1 then // let's try to not explode our Newton, okay? if vLlamaTalk:MBeginTransaction() then begin vLlamaTalk:MWrite("How very like the future this place might be. It is a tiny world just big enough to support the chemicals of one knowledge worker. I feel a wave of loneliness as I head back down. Am I going too fast?"); vLlamaTalk:MWrite("I plunge right on in through the office door and into the bottomless sea below. Suddenly I can't remember how to stop. Turn around. Look. Point behind myself. Do I have to turn around and point? I flip into a burning fit."); vLlamaTalk:MEndTransaction(); end else begin fIsActiveIdle := nil; SetValue(self, 'text, "Start Ping Test"); return nil; end; return 1000; // idle rate at which to send, in miliseconds, or nil to turn off idle handler end, fIsActiveIdle: nil, _proto: protoTextButton, debug: "vSendPings" }; // View vSendPings is declared to vDali vProtocolLabel := /* child of vDali */ {text: "Transport Protocol:", viewBounds: {left: 8, top: 176, right: 112, bottom: 192}, viewJustify: 8388609, _proto: protoStaticText, debug: "vProtocolLabel" }; // View vProtocolLabel is declared to vDali vProtocol := /* child of vDali */ {viewBounds: {left: 120, top: 178, right: 216, bottom: 192}, clusterValue: 1, _proto: protoRadioCluster, debug: "vProtocol" }; // View vProtocol is declared to vDali vADSP := /* child of vProtocol */ {buttonValue: 1, viewBounds: {left: 0, top: -5, right: 48, bottom: 11}, text: "ADSP", _proto: protoRadioButton, debug: "vADSP" }; // View vADSP is declared to vDali vMNP := /* child of vProtocol */ {buttonValue: 2, viewBounds: {left: 48, top: -5, right: 96, bottom: 11}, text: "MNP", _proto: protoRadioButton, debug: "vMNP" }; // View vMNP is declared to vDali vS := /* child of vDali */ {text: "Str", buttonClickScript: func() begin vLlamaTalk:MBeginTransaction(); vLlamaTalk:MWriteObject("Hello"); vLlamaTalk:MEndTransaction(); end, viewBounds: {left: 66, top: 234, right: 94, bottom: 246}, _proto: protoTextButton, debug: "vS" }; // View vS is declared to vDali vC := /* child of vDali */ {text: "Char", buttonClickScript: func() begin vLlamaTalk:MBeginTransaction(); vLlamaTalk:MWriteObject($A); vLlamaTalk:MEndTransaction(); end, viewBounds: {left: 26, top: 234, right: 54, bottom: 246}, _proto: protoTextButton, debug: "vC" }; // View vC is declared to vDali vT := /* child of vDali */ {text: "True", buttonClickScript: func() begin vLlamaTalk:MBeginTransaction(); vLlamaTalk:MWriteObject(true); vLlamaTalk:MEndTransaction(); end, viewBounds: {left: 66, top: 210, right: 94, bottom: 222}, _proto: protoTextButton, debug: "vT" }; // View vT is declared to vDali vI := /* child of vDali */ {text: "Int", buttonClickScript: func() begin vLlamaTalk:MBeginTransaction(); vLlamaTalk:MWriteObject(1234567); vLlamaTalk:MEndTransaction(); end, viewBounds: {left: 26, top: 258, right: 54, bottom: 270}, _proto: protoTextButton, debug: "vI" }; // View vI is declared to vDali vR := /* child of vDali */ {text: "Real", buttonClickScript: func() begin vLlamaTalk:MBeginTransaction(); vLlamaTalk:MWriteObject(1234567.89); vLlamaTalk:MEndTransaction(); end, viewBounds: {left: 66, top: 258, right: 94, bottom: 270}, _proto: protoTextButton, debug: "vR" }; // View vR is declared to vDali vN := /* child of vDali */ {text: "Nil", buttonClickScript: func() begin vLlamaTalk:MBeginTransaction(); vLlamaTalk:MWriteObject(nil); vLlamaTalk:MEndTransaction(); end, viewBounds: {left: 26, top: 210, right: 54, bottom: 222}, _proto: protoTextButton, debug: "vN" }; // View vN is declared to vDali vB := /* child of vDali */ {text: "Binary", buttonClickScript: func() begin vLlamaTalk:MBeginTransaction(); local b := SetLength(SetClass(Clone(""), 'binary), 4); StuffLong(b, 0, 1234567); vLlamaTalk:MWriteObject(b); vLlamaTalk:MEndTransaction(); end, viewBounds: {left: 34, top: 282, right: 86, bottom: 294}, _proto: protoTextButton, debug: "vB" }; // View vB is declared to vDali vAsync := /* child of vDali */ {indent: 4, text: "Connect Asynchronously", viewBounds: {left: 112, top: 192, right: 234, bottom: 208}, viewSetupFormScript: func() begin self.indent := self.indent + StrWidth(self.text); inherited:?viewSetupFormScript(); end, viewValue: nil, _proto: protoRCheckbox, debug: "vAsync" }; // View vAsync is declared to vDali // After Script for "vDali" thisView := vDali; thisView.title := kAppName; // ---- Beginning of section for non used Layout files ---- // End of output