Simbody
Public Member Functions

SimTK::Visualizer::InputSilo Class Reference

This pre-built InputListener is extremely useful for processing user input that is intended to affect a running simulation. More...

#include <Visualizer_InputListener.h>

Inheritance diagram for SimTK::Visualizer::InputSilo:

List of all members.

Public Member Functions

 InputSilo ()
 Default construction is all that is needed; there are no options.
 ~InputSilo ()
 Throws away any unprocessed input.
bool isAnyUserInput () const
 This is a very fast test that does not require locking; you don't have to use this but it is a good idea to do so.
void waitForAnyUserInput () const
 This will wait quietly until the user has provided some input to the VisualizerGUI. Any kind of input will terminate the wait; you'll have to look to see what it was.
bool takeKeyHit (unsigned &key, unsigned &modifiers)
 This will return user key hits until they have all been consumed, in the same order they were received.
void waitForKeyHit (unsigned &key, unsigned &modifiers)
 Same as takeKeyHit() except that if there is no key hit input available it waits until there is, then returns the first one (which is removed from the silo just as takeKeyHit() would do.
bool takeMenuPick (int &menu, int &item)
 This will return user menu picks until they have all been consumed, in the same order they were received.
void waitForMenuPick (int &menu, int &item)
 Same as takeMenuPick() except that if there is no menu pick input available it waits until there is, then returns the first one (which is removed from the silo just as takeMenuPick() would do.
bool takeSliderMove (int &slider, Real &value)
 This will return user changes to slider positions until they have all been consumed, in the same order they were received.
void waitForSliderMove (int &slider, Real &value)
 Same as takeSliderMove() except that if there is no slider move input available it waits until there is, then returns the first one (which is removed from the silo just as takeSliderMove() would do.
void clear ()
 Throw away any pending unprocessed input of all types.

Detailed Description

This pre-built InputListener is extremely useful for processing user input that is intended to affect a running simulation.

The idea is that this object saves up all the user input in a set of "silos", which are first-in-first-out (FIFO) queues. The simulation periodically checks ("polls") to see if there is anything in the silos that needs processing, pulling off one user input at a time until they have all been consumed. This eliminates any need for tricky asynchronous handling of user input, and all thread synchronization issues are handled invisibly.

You can also request to wait quietly until some input arrives, which is useful when you can't proceed without some instruction from the user that you expect to get through the VisualizerGUI.

When the InputSilo receives user input through one of the InputListener methods it implements, it return true indicating that it has processed the input and that no further InputListeners should be called. So if you have other InputListeners that you would like to have called, be sure to add them to the Visulizer prior to adding an InputSilo, which is the last refuge for unwanted user input.

Here's how you can use this:

MultibodySystem system;
// ... build system

// Set up a Visualizer to run in real time mode, and give it an
// InputSilo to gather user input.
Visualizer viz(system);
viz.setMode(Visualizer::RealTime);
InputSilo* silo = new InputSilo;
viz.addInputListener(silo);

// You create a PeriodicEventHandler to poll the input. Note that the interval
// you choose determines how responsive the simulation will be to user input,
// but it also limits the maximum step size that the integrator can take.
system.addEventHandler
    (new MyUserInputHandler(*silo, 0.1)); // check every 100ms 

// Then in MyUserInputHandler::handleEvent(...):
while (silo.isAnyUserInput()) {
    while (silo.takeKeyHit(key,modifier)) {
        // Process the key that was hit
    }
    while (silo.takeMenuPick(which, item)) {
        // Process the picked menu item
    }
    while (silo.takeSliderMove(which, value)) {
        // Process the new value for slider "which"
    }
}

If you want to wait until some input arrives, create the InputSilo and add it to the Visualizer as above, then in your main program (that is, not in the Handler) use code like this:

std::cout << "Hit ENTER in VisualizerGUI to continue ...\n";
unsigned key, modifiers;
do {silo->waitForKeyHit(key,modifiers);}
while (key != Visualizer::InputListener::KeyEnter);

Similar methods are available for all the different input types, and you can also wait on the arrival of any input.

Implementation

The InputSilo implementations of the InputListener methods are called from the Visualizer's listener thread, which is a different thread than the one that is simultaneously running the simulation. The internal silos are double-ended queues (deques) that allow inputs to be pushed onto one end and pulled off the other, so that they can be consumed in FIFO order. There is a single mutex lock associated with all the silos together, and the lock must be held while anything is pushed onto or pulled off of any one of the silos.

Each of the methods for getting the input out of the silos is called from the simulation thread, which must obtain the lock before removing anything, thus safely synchronizing the listener and simulation threads.

A count is maintained of the total number of items in all the silos. It is incremented only when the listener thread holds the lock and adds something to a silo; it is decremented only when the simulation thread holds the lock and pulls something from a silo. The count may be examined without locking; it will have a value that was recently correct and can thus be used for a very fast check on whether there is likely to be any input worth holding a lock for; the isAnyUserInput() method returns true when the count is non-zero. It may occasionally return zero in cases where there is input, but only if that input just arrived so you can safely pick it up on the next poll.

When possible we optimize for the case where many inputs arrive from the same device by just keeping the most recent value. That applies to slider and mouse moves.


Constructor & Destructor Documentation

SimTK::Visualizer::InputSilo::InputSilo ( )

Default construction is all that is needed; there are no options.

SimTK::Visualizer::InputSilo::~InputSilo ( )

Throws away any unprocessed input.


Member Function Documentation

bool SimTK::Visualizer::InputSilo::isAnyUserInput ( ) const

This is a very fast test that does not require locking; you don't have to use this but it is a good idea to do so.

void SimTK::Visualizer::InputSilo::waitForAnyUserInput ( ) const

This will wait quietly until the user has provided some input to the VisualizerGUI. Any kind of input will terminate the wait; you'll have to look to see what it was.

bool SimTK::Visualizer::InputSilo::takeKeyHit ( unsigned &  key,
unsigned &  modifiers 
)

This will return user key hits until they have all been consumed, in the same order they were received.

The key and modifiers values are those that were provided to our implementation of the InputListener::keyPressed() method.

Parameters:
[out]keyThe key code for the key that was hit. See InputListener::KeyCode for interpretation.
[out]modifiersStatus of Shift,Ctrl,Alt and "special" key code. See InputListener::Modifier for interpretation.
Returns:
true if a key and modifiers have been returned; false if the character silo is now empty in which case both key and modifiers will be set to zero.
void SimTK::Visualizer::InputSilo::waitForKeyHit ( unsigned &  key,
unsigned &  modifiers 
)

Same as takeKeyHit() except that if there is no key hit input available it waits until there is, then returns the first one (which is removed from the silo just as takeKeyHit() would do.

The behavior is like calling waitForAnyUserInput() repeatedly until takeKeyHit() returns true.

See also:
takeKeyHit(), waitForAnyUserInput()
bool SimTK::Visualizer::InputSilo::takeMenuPick ( int &  menu,
int &  item 
)

This will return user menu picks until they have all been consumed, in the same order they were received.

The item value returned is the value that was provided to our implementation of the InputListener::menuSelected() method.

Parameters:
[out]menuThe id number of the menu that was selected. This is the value that was assigned to this menu in the Visualizer::addMenu() call.
[out]itemThe menu item number for the entry that the user selected. This is the number that was assigned at the time the menu was added via the Visualizer::addMenu() method.
Returns:
true if a menu item number has been returned; false if the menu pick silo is now empty in which case item will be set to zero.
void SimTK::Visualizer::InputSilo::waitForMenuPick ( int &  menu,
int &  item 
)

Same as takeMenuPick() except that if there is no menu pick input available it waits until there is, then returns the first one (which is removed from the silo just as takeMenuPick() would do.

The behavior is like calling waitForAnyUserInput() repeatedly until takeMenuPick() returns true.

See also:
takeMenuPick(), waitForAnyUserInput()
bool SimTK::Visualizer::InputSilo::takeSliderMove ( int &  slider,
Real &  value 
)

This will return user changes to slider positions until they have all been consumed, in the same order they were received.

The slider and value returns are those that were provided to our implementation of the InputListener::sliderMoved() method.

Parameters:
[out]sliderThe id number of the slider that was moved. This is the value that was assigned to this slider in the Visualizer::addSlider() call.
[out]valueThis is the new value associated with the slider position to which the user moved it.
Returns:
true if a slider move has been returned; false if the slider move silo is now empty in which case which will be set to zero and value will be NaN.
void SimTK::Visualizer::InputSilo::waitForSliderMove ( int &  slider,
Real &  value 
)

Same as takeSliderMove() except that if there is no slider move input available it waits until there is, then returns the first one (which is removed from the silo just as takeSliderMove() would do.

The behavior is like calling waitForAnyUserInput() repeatedly until takeSliderMove() returns true.

See also:
takeSliderMove(), waitForAnyUserInput()
void SimTK::Visualizer::InputSilo::clear ( )

Throw away any pending unprocessed input of all types.


The documentation for this class was generated from the following file:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines