listener (lx_listener.hpp)

From The Foundry MODO SDK wiki
Jump to: navigation, search
There are security restrictions on this page


Event Handling

A large application consists of many interacting subsystems which operate over a shared data model, and changes to the state of the data model may require action by those subsystems which depend on it. Interactions of this kind are best handled by a client-server notification scheme. Clients of some data object register an interest in the object, and when the object changes the server informs all the clients of the change. The clients are called "Listeners".

A listener service allows a client to submit itself as a global listener. The client object is queried for all global listener interfaces and any that are found will subsequently receive notifications as events occur.

(1) SDK: ILxListenerService interface
         LXxMETHOD(  LxResult,
 ScriptQuery) (
         LXtObjectID              self,
         void                   **ppvObj);
 
         LXxMETHOD(  LxResult,
 AddListener) (
         LXtObjectID              self,
         LXtObjectID              object);
 
         LXxMETHOD(  LxResult,
 RemoveListener) (
         LXtObjectID              self,
         LXtObjectID              object);

(2) SDK: Declarations
 #define LXu_LISTENERSERVICE     "1966420D-DFED-11D7-A237-000A9593D716"

Redefine the user method for RemoveListener() so it only fires for real if the interface pointer is non-null. This can happen in rare cases if the listener service is initialized after the system has shut down, like for static listener classes. The higher-level wrappers all handle this more directly.

(3) User Service Class: ListenerService method
         LxResult
 RemoveListener (
         ILxUnknownID             object)
 {
         if (!m_loc)
                 return LXe_NOINTERFACE;
 
         return CLxLoc_ListenerService::RemoveListener (object);
 }

Empty listener service Python user class.

(4) PY: ListenerService method
 pass

Many nexus objects may also support listener ports. By querying for a port interface on an item or scene and adding to that, the listener client will receive events for changes only on that specific object.

(5) SDK: ILxListenerPort interface
         LXxMETHOD(  LxResult,
 AddListener) (
         LXtObjectID              self,
         LXtObjectID              object);
 
         LXxMETHOD(  LxResult,
 RemoveListener) (
         LXtObjectID              self,
         LXtObjectID              object);

(6) SDK: Declarations
 #define LXu_LISTENERPORT        "4FBF5E77-152D-4C4F-BFD4-3F6062CCF6BA"

(7) User Class: ListenerPort method

Empty ListenerPort Python user class.

(8) PY: ListenerPort method
 pass

Since it's relatively common to declare listeners as singletons, we provide a template class to install the listener when needed and remove it when done. This is done using reference counting on the clients. Test lifecycle to prevent listeners from being removed after shutdown in static objects.

(9) User Class: additional
 template <class T>
 class CLxSingletonListener
 {
     public:
         T                       *ref;
         unsigned                 count;
 
         CLxSingletonListener ()
         {
                 ref   = 0;
                 count = 0;
         }
 
         ~CLxSingletonListener ()
         {
                 if (lx::Lifecycle () == LXiLIFECYCLE_AFTER)
                         return;
 
                 while (count)
                         release ();
         }
 
                 void
         acquire ()
         {
                 if (!count)
                 {
                         CLxUser_ListenerService lS;
 
                         ref = new T;
                         lS.AddListener (*ref);
                 }
 
                 count++;
         }
 
                 void
         release ()
         {
                 if (!--count)
                 {
                         CLxUser_ListenerService lS;
 
                         lS.RemoveListener (*ref);
                         delete ref;
                         ref = 0;
                 }
         }
 
         operator       T *  ()          {       return  ref;    }
         operator const T *  ()          {       return  ref;    }
 
               T& operator*  ()          {       return *ref;    }
         const T& operator*  () const    {       return *ref;    }
 
               T* operator-> ()          {       return  ref;    }
         const T* operator-> () const    {       return  ref;    }
 };