]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/include/listener.hh
Merge branch 'master' of /home/jcharles/GIT/Lily/. into translation
[lilypond.git] / lily / include / listener.hh
index 0bc937f2b3b7559e212dd14950aad9d7baf7b121..a13fdc66e14e141d28917e4ffa46d72494b7e740 100644 (file)
 /*
   Listeners
 
-  Listeners are used for stream event dispatching. If you want to
-  register a method as an event handler in a dispatcher, then you
-  must:
+  Listeners are used for stream event dispatching.  The usual way to
+  work with them is to use the GET_LISTENER macro which combines the
+  basic Listener algorithm with a Callback_wrapper structure providing
+  a Scheme handle into a member function.
 
-  - declare the method using the DECLARE_LISTENER macro.
+  To register a member function of Foo as an event handler in a
+  dispatcher, you must:
+
+  - declare the function:
   class Foo
   {
-    DECLARE_LISTENER (method);
+    void method (SCM);
     ...
   };
-  This macro declares the method to take a SCM as parameter, and to
-    return void. It also declares some other stuff that shouldn't be
-    touched.
 
-  - implement the method using IMPLEMENT_LISTENER:
-  IMPLEMENT_LISTENER (Foo, method)
-  void method (SCM e)
+  - implement the method::
+  void Foo::method (SCM e)
   {
     write ("Foo hears an event!");
   }
 
-  - Extract a listener using GET_LISTENER (Foo->method)
+  - Extract a listener using GET_LISTENER (Foomethod)
   - Register the method to the dispatcher using Dispatcher::register
 
   Example:
 
   Foo *foo = (...);
-  Stream_distributor *d = (...);
-  Listener l = GET_LISTENER (foo->method);
-  d->register_listener (l, "EventClass");
+  Dispatcher *d = (...);
+  Listener l = foo->GET_LISTENER (Foo, method);
+  d->add_listener (l, ly_symbol2scm ("EventClass"));
 
   Whenever d hears a stream-event ev of class "EventClass",
   the implemented procedure is called.
 
+  GET_LISTENER actually makes use of a member function
+  get_listener (SCM) available in every Smob<...>-derived class.
+  get_listener receives a function getting an object instance and an
+  event and will turn it into a Listener that will (after turning into
+  Scheme), behave as a function receiving an event as its sole
+  argument, with the object instance being the object from which
+  get_listener was called as a member.
+
+  So (p->get_listener (f)).smobbed_copy () is roughly equivalent to
+  (lambda (ev) (f p->self_scm() ev))
+
   Limitations:
-  - DECLARE_LISTENER currently only works inside smob classes.
+
+  The Callback_wrapper mechanism used in GET_LISTENER works only for
+  classes derived from Smob<...>.
 */
 
+#include "callback.hh"
 #include "smobs.hh"
 
-typedef struct
-{
-  void (*listen_callback) (void *, SCM);
-  void (*mark_callback) (void *);
-} Listener_function_table;
-
-class Listener
+// A listener is essentially any procedure accepting a single argument
+// (namely an event).  The class Listener (or rather a smobbed_copy of
+// it) behaves like such a procedure and is composed of a generic
+// callback function accepting two arguments, namely a "target"
+// (usually an engraver instance) and the event.  Its Scheme
+// equivalent would be
+//
+// (define (make-listener callback target)
+//   (lambda (event) (callback target event)))
+//
+// The class construction is lightweight: as a Simple_smob, this is
+// only converted into Scheme when a smobbed_copy is created.
+
+class Listener : public Simple_smob<Listener>
 {
-  void *target_;
-  Listener_function_table *type_;
+private:
+  SCM callback_;
+  SCM target_;
 public:
-  Listener (const void *target, Listener_function_table *type);
-  Listener (Listener const &other);
-  Listener ();
+  static const char * const type_p_name_;
 
-  void listen (SCM ev) const;
+  Listener (SCM callback, SCM target)
+    : callback_ (callback), target_ (target) { }
+
+  LY_DECLARE_SMOB_PROC (&Listener::listen, 1, 0, 0)
+  SCM listen (SCM ev)
+  {
+    scm_call_2 (callback_, target_, ev);
+    return SCM_UNSPECIFIED;
+  }
+
+  SCM mark_smob () const
+  {
+    scm_gc_mark (callback_);
+    return target_;
+  }
 
   bool operator == (Listener const &other) const
-  { return target_ == other.target_ && type_ == other.type_; }
+  {
+    return scm_is_eq (callback_, other.callback_)
+      && scm_is_eq (target_, other.target_);
+  }
+
+  static SCM equal_p (SCM a, SCM b)
+  {
+    return *unchecked_unsmob (a) == *unchecked_unsmob (b)
+      ? SCM_BOOL_T : SCM_BOOL_F;
+  }
+
+  template <class T, void (T::*callback)(SCM)>
+  static SCM trampoline (SCM target, SCM ev)
+  {
+    T *t = unsmob<T> (target);
+    LY_ASSERT_SMOB (T, target, 1);
 
-  DECLARE_SIMPLE_SMOBS (Listener);
+    (t->*callback) (ev);
+    return SCM_UNDEFINED;
+  }
 };
-DECLARE_UNSMOB (Listener, listener);
-
-#define IMPLEMENT_LISTENER(cl, method)                  \
-void                                                    \
-cl :: method ## _callback (void *self, SCM ev)          \
-{                                                       \
-  cl *s = (cl *)self;                                   \
-  s->method (ev);                                       \
-}                                                       \
-void                                                    \
-cl :: method ## _mark (void *self)                      \
-{                                                       \
-  cl *s = (cl *)self;                                   \
-  scm_gc_mark (s->self_scm ());                         \
-}                                                       \
-Listener                                                \
-cl :: method ## _listener () const                      \
-{                                                       \
-  static Listener_function_table callbacks;             \
-  callbacks.listen_callback = &cl::method ## _callback; \
-  callbacks.mark_callback = &cl::method ## _mark;       \
-  return Listener (this, &callbacks);                   \
-}
-
-#define GET_LISTENER(proc) proc ## _listener ()
-
-#define DECLARE_LISTENER(name)                          \
-  inline void name (SCM);                               \
-  static void name ## _callback (void *self, SCM ev);   \
-  static void name ## _mark (void *self);               \
-  Listener name ## _listener () const
+
+#define GET_LISTENER(cl, proc) get_listener (Callback_wrapper::make_smob<Listener::trampoline<cl, &cl::proc> > ())
 
 #endif /* LISTENER_HH */