Getting Started | Documentation | Glish | Learn More | Programming | Contact Us |
Version 1.9 Build 1556 |
|
As discussed in § 15.2, page , above, Glish clients should construct a single Client object using the argc and argv with which the client program was invoked.
Client objects provide the following public member functions:
The shared flag
indicates if the client should be shared by more than one interpreter.
By default, clients are non-shared, but this flag can be set to
Client::USER
, Client::GROUP
or Client::WORLD
to
indicate that the client should be shared among interpreters started
by a single user, users in the same group, or all users respectively.
If the program was not invoked by the Glish interpreter then the special arguments will be missing. The Client constructor detects this case and knows that the program is running stand-alone. If in this case the -glish argument is given, then the Client reads string-valued events from stdin and ``posts" outbound events to stdout. This behavior allows client programs to be debugged separate from running within Glish. A line such as:
hello there how are you?on stdin turns into a hello event with a value of a four-element string corresponding to "there how are you?".
~
Client()
terminates the client ``cleanly"; that is,
it informs the Glish interpreter that the client terminated successfully
and closes the connection between the client and the interpreter. If
you want the client to indicate ``failure" instead, leading to a
fail event (see § 7.11, page ), then exit the client
program without destructing the Client object.
*tv = 0
, int &timedout = dummy)
waits for the next event to arrive
and returns a pointer to a GlishEvent object representing it.
This object will be Unref()'d on the next call to NextEvent(),
so if you want to keep the GlishEvent pointer (or the Value
pointer) you must Ref() it (or its value element).
You can use the tv parameter to pass in a pointer to a timeval structure that indicates how long NextEvent() should wait for an event before giving up. If the call does time out, the timedout parameter is set to a non-zero value. These parameters have default values and you do not need to worry about them.
If the connection to the interpreter is broken then NextEvent() returns a nil pointer and the caller should delete the Client object and terminate.
Typically this version of PostEvent() is more commonly used than the preceding version.
client->PostEvent( "error", "couldn't open %s", file_name );sends an error event identifying which file could not be opened. In the typical case, the default event context is used.
Some Glish clients need to receive input from multiple sources, such as both user-interface input and event input. The Client class provides three additional member functions to support input multiplexing. The basic idea is that the Client class makes available an fd_set identifying which file descriptors it uses to receive events. This fd_set is then used in a call to select() to determine whether any of the client's event sources are active. Another Client member function takes the fd_set returned by select() and reports whether or not the modified fd_set indicates that an event is pending. If so then a special version of NextEvent() is called with the fd_set passed as an argument; it decodes the fd_set and returns the pending event.
The additional member functions are:
Note that the collection of file descriptors in general changes dynamically, meaning that AddInputMask must be called prior to each call to select() (or at least after every call to NextEvent). Alternatively, you can override the FD_Change virtual member function (see below) to get explicit notification of changes.
The default version of this member function does nothing, so you needn't call it if you override the function in a subclass.
Putting these member functions together, suppose you have an fd_set called mask which already has set in it the non-Glish file descriptors you use for input. The following fragment illustrates how you can multiplex between these input sources and the Glish sources:
// Assume c is a pointer to our Client object. // Add c's input sources into the mask. c->AddInputMask( &mask ); // Now select between the different sources. if ( select( FD_SETSIZE, &mask, 0, 0, 0 ) < 0 ) error(); else { if ( c->HasClientInput( &mask ) ) { GlishEvent* e = c->NextEvent( &mask ); handle_event( e ); } // Check our other input sources for activity, too. ... }
The most recent change to clients is the ability for clients to be shared among multiple interpreters. For this to work, the glishd must be running as root on the machines where shared clients will be running. (See § 16.2.1.) These shared clients allow multiple Glish interpreters to connect to the same Client object. The type of users allowed to connect is controlled by a parameter to the Client constructor. (See § 15.5.1.) Each of the PostEvent() functions take an optional parameter that specifies to which context the event should be sent. And the EventSources() function provides access to all of the sources a given client is managing.
The shared client member functions are:
Below is an example of how these functions work. This client forwards events which it receives to all of the other sources that are connected to it. It is like a multi-way tee connecting its event sources.
int main( int argc, char** argv ) { Client c( argc, argv, Client::GROUP ); for ( GlishEvent* e; (e = c.NextEvent()); ) { source_list &sources = c.EventSources(); loop_over_list( sources, i ) if ( sources[i]->Context() != c.LastContext() ) c.PostEvent( e, sources[i]->Context() ); } return 0; }The Client c, in this example, only accepts connections from other users in the same (UNIX) group as the user group who starts this client.