Copyright ©1995 by NeXT Computer, Inc. All Rights Reserved.
|The Listener class, with the Speaker class, supports communication between applications through Mach messaging. Mach messages are the standard way of performing remote procedure calls (RPCs) in the Mach operating system. The Listener class implements the receiving end of a remote message, and the Speaker class implements the sending end.
Remote messages are sent to ports, which act something like mailboxes for the tasks that have the right to receive the messages delivered there. Each Listener corresponds to a single Mach port to which its application has receive rights. Since a port has a fixed size--usually there's room for only five messages in the port queue--when the port is full, a new message must wait for the Listener to take an old message from the queue.
To initiate a remote message, you send an Objective C message to a Speaker instance. The Speaker translates it into the proper Mach message protocol and dispatches it to the port of the receiving task. The Mach message is received by the Listener instance associated with the port. The Listener verifies that it understands the message, that the Speaker has sent the correct parameters for the message, and that all data values are well formed--for example, that character strings are null-terminated. The Listener translates the Mach message back into an Objective C message, which it sends to itself. It's as if an Objective C message sent to a Speaker in one task is received by a Listener in another task.
The Listener methods that receive remote Objective C messages simply pass those messages on to a delegate. The Listener's job is just to get the message and find another object to respond to it.
The setDelegate: method assigns a delegate to the Listener. There's no default delegate, but before the Application object gets its first event, it registers a Listener for the application and makes itself the Listener's delegate. You can register your own Listener (with Application's setAppListener: method) in start-up code, and when you send the Application object a run message, the Application object will become the Listener's delegate.
If an object has its own delegate when it becomes the Listener's delegate, the Listener looks first to its delegate's delegate and only then to its own delegate when searching for an object to entrust with a remote message. This means that you can implement the methods that respond to remote messages in either the Application object's delegate or in the Application object. (You can also implement the methods directly in a Listener subclass, or in another object you make the Listener's delegate.)
Setting Up a Listener
Two methods, checkInAs: and usePrivatePort, allocate a port for the Listener:
|With the checkInAs: method, the Listener's port is given a name (usually the name of the application) and is registered with the network name server. This makes the port publicly available so that other applications can find it. Applications get send rights to a public port through the NXPortFromName() function.|
|Alternatively, the Listener's port can be kept private (with the usePrivatePort method). Send rights to the port can then be doled out only to selected applications.|
|Once allocated, the port must be added (with the addPort method) to the list of those that the client library monitors. A procedure will automatically be called to read Mach messages from the port queue and begin the Listener's process of transforming the Mach message back into an Objective C message. The procedure is called between events, provided the priority of getting remote messages is at least as high as the priority of getting the next event.
A Listener is typically set up as follows:
myListener = [[Listener alloc] init];
* Sets the object responsible for handling
* messages received.
/* or [myListener usePrivatePort] */
* Now, between events, the client library
* will check to see if a message has arrived
* in the port queue.
. . .
/* When we no longer need the Listener. */
|An application may have more than one Listener and Speaker, but it must have at least one of each to communicate with the Workspace Manager and other applications. If your application doesn't create them, a default Listener and Speaker are created for you at start-up before Application's run method gets the first event.
If a Listener is created for you, it will be checked in automatically under the name returned by Application's appListenerPortName method. Normally, this is the name assigned to the application at compile time. The port will also be added to the list of those the client library monitors, so the Listener will be scheduled to receive messages asynchronously.
The Listener and Speaker classes implement a number of methods that can be used to send and receive remote messages. You can add other methods in Listener and Speaker subclasses. The msgwrap program can be used to generate subclass definitions from a list of method declarations. Most programmers will use msgwrap instead of manually subclassing the Listener class. See the man page for msgwrap for details.
Some remote methods, especially those with the prefix "msg", are designed to allow an application to run under program control rather than user control. By implementing these methods, you'll permit a controlling application to run your application in conjunction with others as part of a script.
Remote messages take two kinds of arguments--input arguments, which pass values from the Speaker to the Listener, and output arguments, which are used to pass values back from the Listener to the Speaker. The Listener sends return information back to the Speaker in a separate Mach message to a port provided by the Speaker. The Speaker reformats this information so that it's returned by reference in variables specified in the original Objective C message.
A method can take up to NX_MAXMSGPARAMS arguments. Arguments are constrained to a limited set of permissible types. Internally, the Listener and Speaker identify each permitted type with a unique character code. Input argument types and their identifying codes are listed below. Note that an array of bytes counts as a single argument, even though two Objective C parameters are used to refer to it--a pointer to the array and an integer that counts the number of bytes in the array. A character string must be null-terminated.
|character string||(char *)||c|
|byte array||(char *), (int)||b|
|receive rights (port)||(port_t)||r|
|send rights (port)||(port_t)||s|
|There's a matching output argument for each of these categories. Since output arguments return information by reference, they're declared as pointers to the respective input types:|
|character string||(char **)||C|
|byte array||(char **), (int *)||B|
|receive rights (port)||(port_t *)||R|
|send rights (port)||(port_t *)||S|
|The validity of all input parameters is guaranteed for the duration of the remote message. The memory allocated for a character string or a byte array is freed automatically after the Listener method returns. If you want to save a string or an array, you must copy it. When the amount of input data is large, you can use the NXCopyInputData() function to take advantage of the out-of-line data feature of Mach messaging. This function is passed the index of the argument to be copied (the combination of a pointer and an integer for a byte array counts as a single argument) and returns a pointer to an area obtained through the vm_allocate() function. This pointer must be freed with vm_deallocate(), rather than free(). Note that the size of the area allocated is rounded up to the next page boundary, and so will be at least one page. Consequently, it is more efficient to malloc() and copy amounts up to about half the page size.
The application is responsible for deallocating all port parameters received with the port_deallocate() function when they're no longer needed.
All remote methods return an int that indicates whether or not the message was successfully transmitted. A return of 0 indicates success.
The Listener methods that receive remote messages use the return value to signal whether they're able to delegate a message to another object. If a method can't entrust its message to the delegate (or the delegate's delegate), it returns a value other than 0. If, on the other hand, it's successful in delegating the message, it passes on the delegate's return value as its own. In general, delegate methods should always return 0.
The Listener doesn't pass the return value back to the Speaker that initiated the remote message. However, if the Speaker is expecting return information from the Listener--that is, if the remote message has output arguments--a nonzero return causes the Listener to send an immediate message back to the Speaker indicating its failure to find a delegate for the remote message. The Speaker method then returns 1.
Note that the return value indicates only whether the message got through; it doesn't say anything about whether the action requested by the message was successfully carried out. To provide that information, a remote message must include an output argument.
|portName||The name under which the port is registered.|
|listenPort||The port where the Listener receives remote messages.|
|signaturePort||The port used to authenticate registration.|
|delegate||The object responsible for responding to remote messages received by the Listener.|
|timeout||How long, in milliseconds, that the Listener will wait for its return results to be placed in the port queue of the sending application.|
|priority||The priority level at which the Listener will receive messages.|
|Initializing the class||+ initialize|
|Initializing a new Listener instance|
|Freeing a Listener||free|
|Setting up a Listener||addPort|
|Providing for program control||msgCalc:|
|Receiving remote messages||messageReceived:|
|Assigning a delegate||setDelegate:|
|Sets up a table that instances of the class use to recognize the remote messages they understand. The table lists the methods that can receive remote messages and specifies the number of parameters for each along with their types. An initialize message is sent to the class the first time it's used; you should never invoke this method.|
|Sets up the necessary conditions for Listener objects to receive remote messages if they're used in applications that don't have an Application object and a main event loop. In other words, if an application doesn't send a run message to the Application object,|
|it will need to send a run message to the Listener class|
|for instances of the class to work. This method never returns, so your application will probably need to be dispatched by messages to its Listener instances.|
|Enables the Listener to receive messages by adding its port to the list of those that the client library monitors. The Listener will then be scheduled to receive messages between events. Returns self.
See also: removePort, DPSAddPort()
|(int)checkInAs:(const char *)name|
|Allocates a port for the Listener, and registers that port as name with the Mach network name server. This method also allocates a signature port that's used to protect the right to remove name from the name server. This method returns 0 if it successfully checks in the application with the name server, and a Mach error code if it doesn't. The Mach error code is most likely to be one of those defined in the header files servers/netname_defs.h and mach/kern_return.h
See also: usePrivatePort, checkOut
|Removes the Listener's port from the list of those registered with the network name server. This makes the port private. This method will always be successful and therefore always returns 0.
See also: checkInAs:
|Returns the Listener's delegate. The default delegate is nil, but just before the first event is received, the Application object is made the delegate of the Listener registered as the Application object's Listener. The delegate is expected to respond to the remote messages received by the Listener, although it may do this by sending messages to another object.
See also: setDelegate:, setAppListener: (Application)
|Frees the Listener object and deallocates its listen port and its signature port. If the Listener's port is registered with the network name server, it is unregistered.|
|Initializes a newly allocated Listener instance. The new instance has no port name, its priority is set to NX_BASETHRESHOLD, its timeout is initialized to 30,000 milliseconds, its listen port and signature port are both PORT_NULL, and it has no delegate. Returns self.
See also: setPriority:, setTimeout:, setDelegate:, checkInAs:
|Returns the port at which the Listener receives remote messages. This port is never set directly, but is allocated by either checkInAs: or usePrivatePort. It's deallocated by the free method. The Listener caches this port as its listenPort instance variable.
See also: checkInAs:, usePrivatePort
|Begins the process of translating a Mach message received at the Listener's port into an Objective C message. This method verifies that the Mach message is well formed, that it corresponds to an Objective C method understood by the Listener, and that the method's arguments agree in number and type with the fields of the Mach message.
messageReceived: messages are initiated whenever a Mach message is to be read from the Listener's port; you shouldn't initiate them in the code you write. Returns self.
See also: performRemoteMethod:paramList:
|Receives a remote message to perform any calculations that are necessary to bring the current window up to date. The method you implement to respond to this message should set the integer specified by flag to YES if the calculations will be performed, and to NO if they won't.|
|(int)msgCopyAsType:(const char *)aType ok:(int *)flag|
|Receives a remote message requesting the application to copy the current selection to the pasteboard as aType data. aType should be one of the standard pasteboard types defined in appkit/Pasteboard.h. The method you implement to respond to this request should set the integer referred to by flag to YES if the selection is copied, and to NO if it isn't.|
|(int)msgCutAsType:(const char *)aType ok:(int *)flag|
|Receives a remote message requesting the application to delete the current selection and place it in the pasteboard as aType data. aType should be one of the standard pasteboard types defined in appkit/Pasteboard.h. The method you implement to respond to this request should set the integer referred to by flag to YES if the requested action is carried out, and to NO if it isn't.|
|(int)msgDirectory:(char *const *)fullPath ok:(int *)flag|
|Receives a remote message asking for the current directory. The method you implement to respond to this message should place a pointer to the full path of its current directory in the variable specified by fullPath. The integer specified by flag should be set to YES if the directory will be provided, and to NO if it won't.
The current directory is application-specific, but is probably best described as the directory the application would show in its Open panel were the user to bring it up.
|(int)msgFile:(char *const *)fullPath ok:(int *)flag|
|Receives a remote message requesting the application to provide the full pathname of its current document. The current document is the file displayed in the main window.
The method you implement to respond to this request should set the pointer referred to by fullPath so that it points to a string containing the full pathname of the current document. The integer specified by flag should be set to YES if the pathname is provided, and to NO if it isn't.
|Receives a remote message requesting the application to replace the current selection with the contents of the pasteboard, just as if the user had chosen the Paste command from the Edit menu. The method you implement to respond to this message should set the integer referred to by flag to YES if the request is carried out, and to NO if it isn't.|
|(int)msgPosition:(char *const *)aString|
|Receives a remote message requesting a description of the current selection.
The method you implement to respond to this request should describe the selection in a character string and set the pointer referred to by aString so that it points the description. The integer referred to by anInt should be set to one of the following constants to indicate how the current selection is described:
|NX_TEXTPOSTYPE||As a character string to search for|
|NX_REGEXPRPOSTYPE||As a regular expression to search for|
|NX_LINENUMPOSTYPE||As a colon-separated range of line numbers, for example "10:12"|
|NX_CHARNUMPOSTYPE||As a colon-separated range of character positions, for example "21:33"|
|NX_APPPOSTYPE||As an application-specific description|
|The integer referred to by flag should be set to YES if the requested information is provided in the other two output arguments, and to NO if it isn't.|
|(int)msgPrint:(const char *)fullPath ok:(int *)flag|
|Receives a remote message requesting the application to print the document whose path is fullPath. The method you implement to respond to this request should set the integer referred to by flag to YES if the document is printed, and to NO if it isn't. The document file should be closed after it's printed.|
|Receives a remote message for the application to quit. The method you implement to respond to this message should set the integer specified by flag to YES if the application will quit, and to NO if it won't.|
|(int)msgSelection:(char *const *)bytes|
asType:(const char *)aType
|Receives a remote message asking the application for its current selection as aType data. aType will be one of the following standard data types for the pasteboard (or an application-specific type):|
|The method you implement to respond to this request should set the pointer referred to by bytes so that it points to the selection and also place the number of bytes in the selection in the integer referred to by numBytes. The integer referred to by flag should be set to YES if the selection is provided, and to NO if it's not.|
|(int)msgSetPosition:(const char *)aString|
|Receives a remote message requesting the application to scroll the current document (the one displayed in the main window) so that the portion described by aString is visible. aString should be interpreted according to the anInt constant, which will be one of the following:|
|NX_TEXTPOSTYPE||aString is a character string to search for.|
|NX_REGEXPRPOSTYPE||aString is a regular expression to search for.|
|NX_LINENUMPOSTYPE||aString is a colon-separated range of line numbers, for example "10:12".|
|NX_CHARNUMPOSTYPE||aString is a colon-separated range of character positions, for example "21:33".|
|NX_APPPOSTYPE||aString is an application-specific description of a portion of the document.|
|The msgSetPosition:posType:andSelect:ok: method you implement should set the integer referred to by flag to YES if the document is scrolled, and to NO if it isn't. If selectFlag is anything other than 0, the portion of the document described by aString should also be selected.|
|(int)msgVersion:(char *const *)aString ok:(int *)flag|
|Receives a remote message requesting the current version of the application. The method you implement to respond to this request should set the pointer referred to by aString so that it points to a string containing current version information for your application. The integer specified by flag should be set to YES if version information is provided, and to NO if it's not.|
|(int)performRemoteMethod:(NXRemoteMethod *)method paramList:(NXParamValue *)params|
|Matches the data received in the Mach message with the corresponding Objective C method and sends the Objective C message to self. The Listener method that receives the message will then try to delegate it to another object. method is a pointer to the method structure returned by remoteMethodFor: and params is a pointer to the list of arguments.
The msgwrap program automatically generates a performRemoteMethod:paramList: method for a Listener subclass. Each Listener subclass must define its own version of the method.
performRemoteMethod:paramList: messages are initiated when the Listener reads a Mach message from its port queue.
See also: remoteMethodFor:, msgwrap(8) UNIX manual page
|(const char *)portName|
|Returns the name under which the Listener's port (the port returned by the listenPort method) is registered with the network name server.
See also: checkInAs:, listenPort, appListenerPortName (Application)
|Returns the priority level for receiving remote messages.
See also: setPriority:
|Reads the Listener from the typed stream stream. Returns self.
See also: write:
|Looks up aSelector in the table of remote messages the Listener understands and returns a pointer to the table entry. A NULL pointer is returned if aSelector isn't in the table.
Each Listener subclass must define its own version of this method and send a message to super to perform the Listener version. The msgwrap program produces subclass method definitions automatically. The version of the method produced by msgwrap uses the NXRemoteMethodFromSel() function to do the look up.
remoteMethodFor: messages are initiated automatically when the Listener reads a Mach message from its port queue.
See also: performRemoteMethod:paramList:, msgwrap(8) UNIX manual page
|Removes the Listener's port from the list of those that the client library monitors. Remote messages sent to the port will pile up in the port queue until they are explicitly read; they won't be read automatically between events.
See also: addPort
|Returns the Listener's services delegate, the object that will respond to remote messages sent from the Services menus of other applications. The services delegate should contain the methods that a service-providing application uses to provide services to other applications.
See also: setServicesDelegate:
|Sets the Listener's delegate to anObject. The delegate is expected to respond to the remote messages received by the Listener. However, if anObject has a delegate of its own at the time the setDelegate: message is sent, the Listener will first check to see if that object can handle a remote message before checking anObject. In other words, the Listener recognizes a chain of delegation.
The delegate assigned by this method will be overridden if the Listener is registered as the Application object's appListener and the assignment is made before the Application object is sent a run message. Before getting the first event, the run method makes the Application object the appListener's delegate.
See also: delegate, setAppListener: (Application)
|Sets the priority for receiving remote messages to level. Whenever the application is ready to get another event, the priority level is compared to the threshold at which the application is asking for the next event. For the Listener to be able to receive remote messages from its port queue, the priority level must be at least equal to the event threshold.
Priority values can range from 0 through 30, but three standard values are generally used:
|These constants are defined in the appkit/Application.h header file.|
|At a priority equal to NX_BASETHRESHOLD, the Listener will be able to receive messages whenever the application asks for an event in the main event loop, but not during a modal loop associated with an attention panel nor during a modal loop associated with a control such as a button or slider.|
|At a priority equal to NX_RUNMODALTHRESHOLD, the Listener will receive remote messages in the main event loop and in the event loop for an attention panel, but not during a control event loop.|
|At a priority equal to NX_MODALRESPTHRESHOLD, remote messages are received even during a control event loop.|
|The default priority level is NX_BASETHRESHOLD.
A new priority takes effect when the Listener receives an addPort message. To change the default, you must either set the Listener's priority before sending it an addPort message, or you must send it a removePort message then another addPort message.
See also: priority, addPort
|Registers anObject as the object within a service provider that will respond to remote messages. This method returns self. As an example, consider an application called Thinker that provides a ThinkAboutIt service that ponders the meaning of ASCII text it receives on the pasteboard. Thinker would need to have something like the following in the _ _services section of its _ _ICON segment in its Mach-O file:|
Send Type: NXAsciiPboardType
Menu Item: ThinkAboutIt
|To get this information in your Mach-O file you could put the above text in a file called services.txt and then include the following line in your Makefile.preamble file:|
LDFLAGS = -segcreate _ _ICON _ _services services.txt
|Alternatively, if the services the application can provide are not known at compile time, the application can build a services file at run time; see NXUpdateDynamicServices().
Then, in order to provide the ThinkAboutIt service you must implement a thinkMethod:userData:error: method in an object which is the services delegate of a Listener which is listening on the Thinker port. (If the application is named "Thinker", then by default NXApp's Listener listens on this port.) Here is an example method that could be used to provide the ThinkAboutIt service:
userData:(const char *)userData
char *const *s; /* We use s to go through types. */
char *const *types = [pb types];
for (s = types; *s; s++)
if (!strcmp(*s, NXAsciiPboardType)) break;
if (*s && [pb readType:NXAsciiPboardType
/* doSomething is your own method... */
[self doSomething:data :length];
/* free the memory allocated by readType:... */
vm_deallocate(task_self(), data, length);
/* now make msg point to an error string if */
/* anything went wrong, and return... */
|See also: servicesDelegate, registerServicesMenuSendTypes:andReturnTypes: (Application), validRequestorForSendType:andReturnType: (Responder)|
|Sets, to ms milliseconds, how long the Listener will persist in attempting to send a return message back to the Speaker that initiated the remote message. If ms is 0, there will be no time limit. The default is 30,000 milliseconds. Returns self.
See also: timeout
|Returns the port that's used to authenticate the Listener's port to the network name server. This port is never set directly, but is allocated by checkInAs: and deallocated by free.
See also: checkInAs:, free, netname_check_in(), netname_check_out()
|Returns the number of milliseconds the Listener will wait for a return message to the Speaker to be successfully placed in the port designated by the Speaker. If it's 0, there's no time limit.
See also: setTimeout:
|Allocates a listening port for the Listener, but doesn't register it publicly. Other tasks can send messages to this Listener only if they are explicitly given the address of the port in a message; the port is not available through the Network Name Server. This method is an alternative to checkInAs:. It returns 0 on success and a Mach error code if it can't allocate the port. The error code will be one of those defined in mach/kern_return.h.
See also: checkInAs:
|Writes the Listener to the typed stream stream. Returns self.
See also: read: