Hi, Sorry for the delay. Here's a new revision of the dispatcher system. Known issues: - Some trivial changes should be done to other files, e.g. lily.scm and lily-proto.hh. - I added the unique_ member to Context. It's just an int that's supposed to be unique for each context. unique might not be the best name; suggestios for better names are welcome.
And some comments: On Wednesday 05 April 2006 14.10, Han-Wen Nienhuys wrote: > Erik Sandberg wrote: > > Some known issues: > > - scm/define-event-classes.scm contains rather unsorted functions which > > are > > i'm missing that file. sorry, attached. > > - The Stream_event class duplicates its 'context property with a context_ > > member; this was originally intended to give speedups, but it is broken > > in this version and requires some modifications to Context in order to > > work. I'll probably remove the context_ member altogether in the next > > revision. > > yes please do. > > > /* > > Event dispatching: > > - Collect a list of listeners for each relevant class > > - Send the event to each of these listeners, in increasing priority > > order. This is done by keeping a bubble-sorted temporary list of listener > > lists, and iteratively send the event to the lowest-priority listener. - > > An event is never sent twice to listeners with equal priority. */ > > IMPLEMENT_LISTENER (Dispatcher, dispatch) (Stream_event *ev) > > { > > SCM class_symbol = ev->get_property ("class"); > > if (!scm_symbol_p (class_symbol)) > > { > > warning (_f ("Unknown event class %s", ly_symbol2string > > (class_symbol).c_str ())); return; > > } > > > > SCM class_list = scm_primitive_eval (class_symbol); > > ugh. WTF is this? Where does this come from, in what module should it be > defined. Why does this do an eval() for every dispatch() call? I used eval as a poor man's hashq. I have cleaned it up a bit now, by abstracting the eval call. > > bool sent = false; > > > > // TODO: fix this loop. > > int num_classes = 0; > > for (SCM cl = class_list; cl != SCM_EOL; cl = scm_cdr (cl)) > > num_classes++; > > scm_ilength thanks > > // Collect all listener lists. > > struct { int prio; SCM list; } lists[num_classes+1]; > > int i = 0; > > for (SCM cl = class_list; cl != SCM_EOL; cl = scm_cdr (cl)) > > { > > SCM list = scm_hashq_ref (listeners_, scm_car (cl), SCM_EOL); > > if (list == SCM_EOL) > > num_classes--; > > else > > { > > // bubblesort. > > int prio = scm_to_int (scm_caar (list)); > > int j; > > for (j = i; j > 0 && lists[j-1].prio > prio; j--) > > lists[j] = lists[j-1]; > > lists[j].prio = prio; > > lists[j].list = list; > > i++; > > } > > } > > lists[num_classes].prio = INT_MAX; > > can you use a Scheme sort routine to do this? > > do I understand correctly that for every time step, we get multiple > bubble sorts? That doesn't look very clean? The bubble sorts are just a primitive implementation of a priority queue. The queue typically has two elements (the height of the event-class tree), so I felt that using pure C and simple bubble-sort would be the most efficient way to do it. The main reason for C is that the stack is used for memory allocation; I suspect this would be much slower in guile due to GC. > > #if 0 > > /* > > New listeners are appended to the end of the list. > > This way, listeners will listen to an event in the order they were > > added. */ > > why if 0 ? sorry, obsolete code > > // We just remove the listener once. > > bool first = true; > > > > SCM dummy = scm_cons (SCM_EOL, list); > > SCM e = dummy; > > while (scm_cdr (e) != SCM_EOL) > > if (*unsmob_listener (scm_cdadr (e)) == l && first) > > { > > scm_set_cdr_x (e, scm_cddr(e)); > > first = false; > > break; > > } > > else > > e = scm_cdr (e); > > list = scm_cdr (dummy); > > try to use scm_delq or similar, if not possible, devise an appropriate > del() routine yoursefl. Thanks, scm_delete seems to do the job (I didn't realise I had defined an equality predicate) > > #ifndef NDEBUG > > // assert (SCM_EOL == scm_hashq_ref (listeners_, ly_symbol2scm > > ("StreamEvent"), SCM_EOL)); #endif > > idem. > > > LY_DEFINE (ly_make_dispatcher, "ly:make-dispatcher", > > as a matter of style, this should be in dispatcher-scheme.cc > > > /* > > listener-scheme.cc -- Connect listeners to Scheme through Scm_listener > > > > source file of the GNU LilyPond music typesetter > > > > (c) 2005-2006 Erik Sandberg <[EMAIL PROTECTED]> > > */ > > > > #include "listener.hh" > > #include "ly-smobs.icc" > > #include "stream-event.hh" > > > > class Scm_listener > > this should be in scm-listener.cc > > > { > > public: > > Scm_listener (SCM callback); > > DECLARE_LISTENER (listener); > > protected: > > DECLARE_SMOBS (Scm_listener,); > > private: > > SCM callback_; > > }; > > > > IMPLEMENT_LISTENER (Scm_listener, listener) (Stream_event *ev) > > Please change the def of this macro so we can have > > IMPLEMENT_LISTENER (Scm_listener, listener); > Scm_listener::real_declaration (Stream_event *) > > otherwise tools like TAGS get very confused. I have changed the definition to: IMPLEMENT_LISTENER (Scm_listener, listener, (Stream_event *ev)) { ... } Your suggestion doesn't work well because of some magic inside the macro. > > LY_DEFINE (ly_make_listener, "ly:make-listener", > > scm-listener-scheme.cc Scm_listener is only intended to be used locally by that function; splitting the file into two modules would feel artificial/meaningless. Perhaps I should rename the class to Listener_scheme? > > // ES todo: Add stuff to lily-proto.hh: Stream_event and its subclasses, > > Stream_creator, etc. > > yes. in any case, I'm missing a patch. > > > SCM > > Stream_event::internal_get_property (SCM sym) const > > { > > SCM s = scm_sloppy_assq (sym, property_alist_); > > if (s != SCM_BOOL_F) > > return scm_cdr (s); > > return SCM_EOL; > > } > > you might want to consider basing these objects on Prob; see prob.cc Is there a point in doing that? There are no immutable properties > > #define SEND_EVENT_TO_CONTEXT(ctx, cl, ...) \ > > { \ > > Stream_event *_e_ = new Stream_event (ctx, ly_symbol2scm (cl)); \ > > __VA_ARGS__; \ > > ctx->event_source ()->distribute (_e_); \ > > scm_gc_unprotect_object (_e_->self_scm ()); > > \ > > } > > > > #define EVENT_PROPERTY(prop, val) \ > > (_e_->set_property (prop, val)) > > what's this? Is it ever used? It looks fishy anyway. It's used about 15 times so far. It's a shorthand for creating and reporting an event; e.g., the following code generates and broadcasts a Revert event: SEND_EVENT_TO_CONTEXT (get_outlet (), "Revert", EVENT_PROPERTY("symbol", sym), EVENT_PROPERTY("property", eprop)); > In general, it seems that Dispatcher is not connected to Stream_event at > all. Why not make I guess you mean listeners. I have now generalised them. -- Erik
;;;; stream-event-classes.scm -- define the tree of stream-event classes. ;;;; ;;;; source file of the GNU LilyPond music typesetter ;;;; ;;;; (c) 2005-2006 Erik Sandberg <[EMAIL PROTECTED]> (use-modules (srfi srfi-1)) ;; Event class hierarchy. Each line is on the form ((List of children) . Parent) (define event-classes '(((StreamEvent) . '()) ((RemoveContext ChangeParent Override Revert UnsetProperty SetProperty MusicEvent CreateContext Prepare OneTimeStep Finish) . StreamEvent) )) ;; Each class will be defined as ;; (class parent grandparent .. ) ;; so that (eq? (cdr class) parent) holds. (for-each (lambda (rel) (for-each (lambda (type) (primitive-eval `(define ,type (cons ',type ,(cdr rel))))) (car rel))) event-classes) ;; TODO: Allow entering more complex classes, by taking unions. (define-public (ly:make-event-class leaf) (primitive-eval leaf) (defmacro-public make-stream-event (expr) (Stream_event::undump (primitive-eval (list 'quasiquote expr)))) (define* (simplify e) (cond ;; Special case for lists reduces stack consumption. ((list? e) (map simplify e)) ((pair? e) (cons (simplify (car e)) (simplify (cdr e)))) ((ly:stream-event? e) (list 'unquote `(make-stream-event ,(simplify (Stream_event::dump e))))) ((ly:music? e) (list 'unquote (music->make-music e))) ((ly:moment? e) (list 'unquote `(ly:make-moment ,(ly:moment-main-numerator e) ,(ly:moment-main-denominator e) . ,(if (eq? 0 (ly:moment-grace-numerator e)) '() (list (ly:moment-grace-numerator e) (ly:moment-grace-denominator e)))))) ((ly:duration? e) (list 'unquote `(ly:make-duration ,(ly:duration-log e) ,(ly:duration-dot-count e) ,(car (ly:duration-factor e)) ,(cdr (ly:duration-factor e))))) ((ly:pitch? e) (list 'unquote `(ly:make-pitch ,(ly:pitch-octave e) ,(ly:pitch-notename e) ,(ly:pitch-alteration e)))) ((ly:input-location? e) (list 'unquote '(ly:dummy-input-location))) (#t e))) (define-public (ly:simplify-scheme e) (list 'quasiquote (simplify e)) ) ; used by lily/dispatcher.cc (define-public (car< a b) (< (car a) (car b)))
Index: context.cc =================================================================== RCS file: /sources/lilypond/lilypond/lily/context.cc,v retrieving revision 1.78 diff -u -r1.78 context.cc --- context.cc 12 Feb 2006 16:40:02 -0000 1.78 +++ context.cc 26 Apr 2006 18:55:29 -0000 @@ -96,6 +96,7 @@ accepts_list_ = SCM_EOL; context_list_ = SCM_EOL; definition_ = SCM_EOL; + unique_ = -1; smobify_self (); @@ -236,6 +237,7 @@ Context *new_context = cdef->instantiate (ops, key); + new_context->unique_ = get_global_context()->new_unique(); new_context->id_string_ = id; add_context (new_context); apply_property_operations (new_context, ops); Index: global-context.cc =================================================================== RCS file: /sources/lilypond/lilypond/lily/global-context.cc,v retrieving revision 1.40 diff -u -r1.40 global-context.cc --- global-context.cc 31 Jan 2006 00:30:43 -0000 1.40 +++ global-context.cc 26 Apr 2006 18:55:29 -0000 @@ -204,3 +204,9 @@ else return Context::get_default_interpreter (); } + +int +Global_context::new_unique () +{ + return ++unique_count_; +} Index: include/context.hh =================================================================== RCS file: /sources/lilypond/lilypond/lily/include/context.hh,v retrieving revision 1.38 diff -u -r1.38 context.hh --- include/context.hh 16 Feb 2006 11:54:21 -0000 1.38 +++ include/context.hh 26 Apr 2006 18:55:29 -0000 @@ -29,6 +29,7 @@ friend class Context_handle; int iterator_count_; bool init_; + int unique_; protected: Context *daddy_context_; @@ -54,6 +55,7 @@ string id_string () const { return id_string_; } SCM children_contexts () const { return context_list_; } SCM default_child_context_name () const; + int get_unique() { return unique_; } Translator_group *implementation () const { return implementation_; } Context *get_parent_context () const; Index: include/global-context.hh =================================================================== RCS file: /sources/lilypond/lilypond/lily/include/global-context.hh,v retrieving revision 1.13 diff -u -r1.13 global-context.hh --- include/global-context.hh 6 Jan 2006 09:13:24 -0000 1.13 +++ include/global-context.hh 26 Apr 2006 18:55:29 -0000 @@ -16,6 +16,7 @@ { PQueue<Moment> extra_mom_pq_; Output_def *output_def_; + int unique_count_; DECLARE_CLASSNAME(Global_context); @@ -39,6 +40,7 @@ virtual Moment now_mom () const; virtual Context *get_default_interpreter (); + int new_unique (); Moment previous_moment () const; protected: Moment final_mom_;
/* dispatcher.cc -- implement Dispatcher source file of the GNU LilyPond music typesetter (c) 2005-2006 Erik Sandberg <[EMAIL PROTECTED]> */ #include "dispatcher.hh" #include "international.hh" #include "ly-smobs.icc" #include "stream-event.hh" #include "warn.hh" // ES todo: move to lily-guile.hh SCM appendable_list (); void appendable_list_append (SCM l, SCM elt); IMPLEMENT_SMOBS (Dispatcher); IMPLEMENT_TYPE_P (Dispatcher, "dispatcher"); IMPLEMENT_DEFAULT_EQUAL_P (Dispatcher); Dispatcher::~Dispatcher () { } Dispatcher::Dispatcher () { self_scm_ = SCM_EOL; listeners_ = SCM_EOL; dispatchers_ = SCM_EOL; listen_classes_ = SCM_EOL; smobify_self (); //TODO use resizable hash listeners_ = scm_c_make_hash_table (17); priority_count_ = 0; } SCM Dispatcher::mark_smob (SCM sm) { Dispatcher *me = (Dispatcher *) SCM_CELL_WORD_1 (sm); scm_gc_mark (me->dispatchers_); scm_gc_mark (me->listen_classes_); return me->listeners_; } int Dispatcher::print_smob (SCM s, SCM p, scm_print_state*) { Dispatcher *me = (Dispatcher *) SCM_CELL_WORD_1 (s); scm_puts ("#<Dispatcher ", p); scm_write (scm_vector_to_list (me->listeners_), p); scm_puts (">", p); return 1; } /* Event dispatching: - Collect a list of listeners for each relevant class - Send the event to each of these listeners, in increasing priority order. This is done by keeping a bubble-sorted temporary list of listener lists, and iteratively send the event to the lowest-priority listener. - An event is never sent twice to listeners with equal priority. */ IMPLEMENT_LISTENER (Dispatcher, dispatch, (SCM sev)) { Stream_event *ev = unsmob_stream_event (sev); SCM class_symbol = ev->get_property ("class"); if (!scm_symbol_p (class_symbol)) { warning (_f ("Unknown event class %s", ly_symbol2string (class_symbol).c_str ())); return; } SCM class_list = scm_call_1 (ly_lily_module_constant ("ly:make-event-class"), class_symbol); bool sent = false; int num_classes = scm_ilength (class_list); // Collect all listener lists. struct { int prio; SCM list; } lists[num_classes+1]; int i = 0; for (SCM cl = class_list; scm_is_pair(cl); cl = scm_cdr (cl)) { SCM list = scm_hashq_ref (listeners_, scm_car (cl), SCM_EOL); if (!scm_is_pair(list)) num_classes--; else { // bubblesort. int prio = scm_to_int (scm_caar (list)); int j; for (j = i; j > 0 && lists[j-1].prio > prio; j--) lists[j] = lists[j-1]; lists[j].prio = prio; lists[j].list = list; i++; } } lists[num_classes].prio = INT_MAX; // Never send an event to two listeners with equal priority. int last_priority = -1; // Iteratively process all event classes, in increasing priority. while (num_classes) { // Send the event, if we haven't already sent it to this target. if (lists[0].prio != last_priority) { // process the listener assert (lists[0].prio > last_priority); last_priority = lists[0].prio; Listener *l = unsmob_listener (scm_cdar (lists[0].list)); l->listen (ev->self_scm ()); sent = true; } // go to the next listener; bubble-sort the class list. SCM next = scm_cdr (lists[0].list); if (!scm_is_pair(next)) num_classes--; int prio = (scm_is_pair(next)) ? scm_to_int (scm_caar (next)) : INT_MAX; for (i = 0; prio > lists[i+1].prio; i++) lists[i] = lists[i+1]; lists[i].prio = prio; lists[i].list = next; } if (!sent) warning (_f ("Junking event: %s", ly_symbol2string (class_symbol).c_str ())); } void Dispatcher::broadcast (Stream_event *ev) { dispatch_proc (ev->self_scm ()); } void Dispatcher::add_listener (Listener l, SCM ev_class) { internal_add_listener (l, ev_class, ++priority_count_); } inline void Dispatcher::internal_add_listener (Listener l, SCM ev_class, int priority) { SCM list = scm_hashq_ref (listeners_, ev_class, SCM_EOL); if (list == SCM_EOL) { /* Register with all dispatchers. */ for (SCM disp = dispatchers_; scm_is_pair(disp); disp = scm_cdr (disp)) { int priority = scm_to_int (scm_cdar (disp)); Dispatcher *d = unsmob_dispatcher (scm_caar (disp)); d->internal_add_listener (dispatch (), ev_class, priority); } listen_classes_ = scm_cons (ev_class, listen_classes_); } SCM entry = scm_cons (scm_int2num (priority), l.smobbed_copy ()); list = scm_merge_x (list, scm_list_1 (entry), ly_lily_module_constant ("car<")); scm_hashq_set_x (listeners_, ev_class, list); } void Dispatcher::remove_listener (Listener l, SCM ev_class) { SCM list = scm_hashq_ref (listeners_, ev_class, SCM_EOL); if (list == SCM_EOL) { programming_error ("remove_listener called with incorrect class."); return; } // We just remove the listener once. bool first = true; SCM dummy = scm_cons (SCM_EOL, list); SCM e = dummy; while (scm_is_pair(scm_cdr (e))) if (*unsmob_listener (scm_cdadr (e)) == l && first) { scm_set_cdr_x (e, scm_cddr(e)); first = false; break; } else e = scm_cdr (e); list = scm_cdr (dummy); if (first) warning ("Attempting to remove nonexisting listener."); else if (list == SCM_EOL) { /* Unregister with all dispatchers. */ for (SCM disp = dispatchers_; disp != SCM_EOL; disp = scm_cdr (disp)) { Dispatcher *d = unsmob_dispatcher (scm_caar (disp)); d->remove_listener (dispatch (), ev_class); } listen_classes_ = scm_delq_x (ev_class, listen_classes_); } } /* Register as a listener to another dispatcher. */ void Dispatcher::register_as_listener (Dispatcher *disp) { int priority = ++disp->priority_count_; // Don't register twice to the same dispatcher. if (scm_assq (disp->self_scm (), dispatchers_) != SCM_BOOL_F) { warning ("Already listening to dispatcher, ignoring request"); return; } dispatchers_ = scm_acons (disp->self_scm (), scm_int2num (priority), dispatchers_); Listener list = dispatch (); for (SCM cl = listen_classes_; cl != SCM_EOL; cl = scm_cdr (cl)) { disp->internal_add_listener (list, scm_car (cl), priority); } } /* Unregister as a listener to another dispatcher. */ void Dispatcher::unregister_as_listener (Dispatcher *disp) { dispatchers_ = scm_assq_remove_x (dispatchers_, disp->self_scm ()); Listener list = dispatch (); for (SCM cl = listen_classes_; cl != SCM_EOL; cl = scm_cdr (cl)) { disp->remove_listener (list, scm_car (cl)); } }
/* stream-event.cc -- implement Stream_event source file of the GNU LilyPond music typesetter (c) 2005-2006 Erik Sandberg <[EMAIL PROTECTED]> */ #include "stream-event.hh" #include "ly-smobs.icc" #include "context.hh" #include "input.hh" #include "input-smob.hh" // ES todo: Add stuff to lily-proto.hh: Stream_event and its subclasses, Stream_creator, etc. Stream_event::~Stream_event () { } void Stream_event::init () { self_scm_ = SCM_EOL; property_alist_ = SCM_EOL; origin_ = 0; smobify_self (); } Stream_event::Stream_event () { init (); } Stream_event::Stream_event (Context *c, Input *origin) { init (); set_property ("context", scm_int2num (c->get_unique())); origin_ = origin; } Stream_event::Stream_event (SCM property_alist) { init (); property_alist_ = property_alist; origin_ = &dummy_input_global; } Stream_event::Stream_event (Context *c, SCM class_name) { init (); set_property ("context", scm_int2num (c->get_unique())); set_property ("class", class_name); origin_ = &dummy_input_global; } Stream_event::Stream_event (Stream_event *ev) { init (); property_alist_ = scm_copy_tree (ev->property_alist_); origin_ = ev->origin_; } Input * Stream_event::origin () const { return origin_; } SCM Stream_event::mark_smob (SCM sm) { Stream_event *me = (Stream_event *) SCM_CELL_WORD_1 (sm); return me->property_alist_; } int Stream_event::print_smob (SCM s, SCM port, scm_print_state *) { scm_puts ("#<Stream_event ", port); scm_write (dump (s), port); scm_puts (" >", port); return 1; } IMPLEMENT_SMOBS (Stream_event); IMPLEMENT_DEFAULT_EQUAL_P (Stream_event); IMPLEMENT_TYPE_P (Stream_event, "ly:stream-event?"); MAKE_SCHEME_CALLBACK (Stream_event, undump, 1); MAKE_SCHEME_CALLBACK (Stream_event, dump, 1); SCM Stream_event::dump (SCM self) { Stream_event *ev = unsmob_stream_event (self); // Reversed alists look prettier. return scm_reverse (ev->property_alist_); } SCM Stream_event::undump (SCM data) { Stream_event *obj = new Stream_event (); obj->property_alist_ = scm_reverse (data); return obj->unprotect (); } SCM Stream_event::internal_get_property (SCM sym) const { SCM s = scm_sloppy_assq (sym, property_alist_); if (s != SCM_BOOL_F) return scm_cdr (s); return SCM_EOL; } void Stream_event::internal_set_property (SCM prop, SCM val) { property_alist_ = scm_assq_set_x (property_alist_, prop, val); }
/* stream-event.cc -- implement Scheme bindings for Stream_event source file of the GNU LilyPond music typesetter (c) 2006 Erik Sandberg <[EMAIL PROTECTED]> */ #include "stream-event.hh" LY_DEFINE (ly_make_stream_event, "ly:make-stream-event", 1, 0, 0, (SCM proplist), "Creates a stream event, with the given property list.\n" ) { SCM_ASSERT_TYPE (scm_list_p (proplist), proplist, SCM_ARG1, __FUNCTION__, "association list"); Stream_event *e = new Stream_event (proplist); return e->unprotect (); } LY_DEFINE (ly_stream_event_property, "ly:stream-event-property", 2, 0, 0, (SCM sev, SCM sym), "Get the property @var{sym} of stream event @var{mus}.\n" "If @var{sym} is undefined, return @code{' ()}.\n") { Stream_event *e = unsmob_stream_event (sev); SCM_ASSERT_TYPE (e, sev, SCM_ARG1, __FUNCTION__, "stream event"); SCM_ASSERT_TYPE (scm_is_symbol (sym), sym, SCM_ARG2, __FUNCTION__, "symbol"); return e->internal_get_property (sym); }
/* dispatcher.cc -- implement Scheme bindings for Dispatcher source file of the GNU LilyPond music typesetter (c) 2006 Erik Sandberg <[EMAIL PROTECTED]> */ #include "dispatcher.hh" LY_DEFINE (ly_make_dispatcher, "ly:make-dispatcher", 0, 0, 0, (), "Returns a newly created dispatcher.") { return (new Dispatcher ())->unprotect (); } LY_DEFINE (ly_register_dispatcher, "ly:connect-dispatchers", 2, 0, 0, (SCM to, SCM from), "Makes the dispatcher @var{to} listen to events from @var{from}." ) { Dispatcher *t = unsmob_dispatcher (to); Dispatcher *f = unsmob_dispatcher (from); SCM_ASSERT_TYPE (t, from, SCM_ARG1, __FUNCTION__, "dispatcher"); SCM_ASSERT_TYPE (f, to, SCM_ARG2, __FUNCTION__, "dispatcher"); t->register_as_listener (f); return SCM_UNDEFINED; } LY_DEFINE (ly_add_listener, "ly:add-listener", 2, 0, 1, (SCM list, SCM disp, SCM cl), "Adds the listener @var{list} to the dispatcher @var{disp}.\n" " Whenever @var{disp} hears an event of class @var{cl}, it will be forwarded to @var{list}.\n" ) { Listener *l = unsmob_listener (list); Dispatcher *d = unsmob_dispatcher (disp); SCM_ASSERT_TYPE (l, list, SCM_ARG1, __FUNCTION__, "listener"); SCM_ASSERT_TYPE (d, disp, SCM_ARG2, __FUNCTION__, "dispatcher"); for (int arg=SCM_ARG3; cl != SCM_EOL; cl = scm_cdr (cl), arg++) { SCM_ASSERT_TYPE (scm_symbol_p (cl), cl, arg, __FUNCTION__, "symbol"); d->add_listener (*l, scm_car (cl)); } return SCM_UNDEFINED; } LY_DEFINE (ly_broadcast, "ly:broadcast", 2, 0, 0, (SCM disp, SCM ev), "Sends the stream event @var{ev} to the dispatcher\n" "@var{disp}.") { Dispatcher *d = unsmob_dispatcher (disp); Stream_event *e = unsmob_stream_event (ev); SCM_ASSERT_TYPE (d, disp, SCM_ARG1, __FUNCTION__, "dispatcher"); SCM_ASSERT_TYPE (e, ev, SCM_ARG2, __FUNCTION__, "stream event"); d->broadcast (e); return SCM_UNDEFINED; }
/* listener-scheme.cc -- Connect listeners to Scheme through Scm_listener source file of the GNU LilyPond music typesetter (c) 2005-2006 Erik Sandberg <[EMAIL PROTECTED]> */ #include "listener.hh" #include "ly-smobs.icc" #include "stream-event.hh" class Scm_listener { public: Scm_listener (SCM callback); DECLARE_LISTENER (listener); protected: DECLARE_SMOBS (Scm_listener,); private: SCM callback_; }; IMPLEMENT_LISTENER (Scm_listener, listener, (SCM ev)) { scm_call_1 (callback_, ev); } IMPLEMENT_SMOBS (Scm_listener); IMPLEMENT_DEFAULT_EQUAL_P (Scm_listener); Scm_listener::Scm_listener (SCM c) { callback_ = SCM_EOL; self_scm_ = SCM_EOL; smobify_self (); callback_ = c; } SCM Scm_listener::mark_smob (SCM obj) { Scm_listener *me = (Scm_listener *) SCM_CELL_WORD_1 (obj); return me->callback_; } int Scm_listener::print_smob (SCM obj, SCM p, scm_print_state*) { Scm_listener *me = (Scm_listener *) SCM_CELL_WORD_1 (obj); scm_puts ("#<Scm_listener ", p); scm_write (me->callback_, p); scm_puts (">", p); return 1; } Scm_listener::~Scm_listener () { } LY_DEFINE (ly_make_listener, "ly:make-listener", 1, 0, 0, (SCM callback), "Creates a listener. Any time the listener hears\n" " an object, it will call @var{callback}\n" " with that object. @var{callback} should take exactly one argument." ) { SCM_ASSERT_TYPE (scm_procedure_p (callback), callback, SCM_ARG1, __FUNCTION__, "procedure"); Scm_listener *l = new Scm_listener (callback); SCM listener = l->listener ().smobbed_copy (); l->unprotect (); return listener; }
/* listener.cc -- implement Listener and Listener_target source file of the GNU LilyPond music typesetter (c) 2005 Erik Sandberg <[EMAIL PROTECTED]> */ #include "listener.hh" #include "ly-smobs.icc" #include "warn.hh" /* Listener_target::~Listener_target () { } */ Listener::Listener (const void *target, Listener_function_table *type) { target_ = (void *)target; type_ = type; } Listener::Listener (Listener const &other) { target_ = other.target_; type_ = other.type_; } void Listener::listen (SCM ev) const { (type_->listen_callback) (target_, ev); } SCM Listener::mark_smob (SCM sm) { Listener *me = (Listener *) SCM_CELL_WORD_1 (sm); (me->type_->mark_callback) (me->target_); return SCM_EOL; } int Listener::print_smob (SCM s, SCM p, scm_print_state*) { scm_puts ("#<Listener>", p); return 1; } SCM Listener::equal_p (SCM a, SCM b) { Listener *l1 = unsmob_listener (a); Listener *l2 = unsmob_listener (b); return (*l1 == *l2) ? SCM_BOOL_T : SCM_BOOL_F; } IMPLEMENT_SIMPLE_SMOBS (Listener); IMPLEMENT_TYPE_P (Listener, "listener");
/* listener.hh -- declare Listener source file of the GNU LilyPond music typesetter (c) 2005 Erik Sandberg <[EMAIL PROTECTED]> */ #ifndef LISTENER_HH #define LISTENER_HH /* 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: - declare the method using DECLARE_LISTENER: class Foo { DECLARE_LISTENER (method); ... }; - implement the method using IMPLEMENT_LISTENER: IMPLEMENT_LISTENER (Foo, method, (Stream_event *e)) { write ("Foo hears an event!"); } - register the method to the dispatcher using Dispatcher::register Foo *foo = (...); Stream_distributor *d = (...); Listener l = foo->method (); d->register_listener (l, "EventClass"); Whenever d hears a stream-event ev of class "EventClass", the implemented procedure is called. DECLARE_LISTENER currently only works inside smob classes. */ #include "smobs.hh" /*TODO: to lily-proto*/ class Stream_event; typedef struct { void (*listen_callback) (void *, SCM); void (*mark_callback) (void *); } Listener_function_table; class Listener { void *target_; Listener_function_table *type_; public: Listener (const void *target, Listener_function_table *type); Listener (Listener const &other); void listen (SCM ev) const; bool operator == (Listener const &other) const { return target_ == other.target_ && type_ == other.type_; } DECLARE_SIMPLE_SMOBS (Listener,); }; DECLARE_UNSMOB (Listener, listener); #define IMPLEMENT_LISTENER(cl, method, params) \ void \ cl :: method ## _callback (void *self, SCM ev) \ { \ cl *s = (cl *)self; \ s->method ## _proc (ev); \ } \ void \ cl :: method ## _mark (void *self) \ { \ cl *s = (cl *)self; \ scm_gc_mark (s->self_scm ()); \ } \ Listener \ cl :: method () const \ { \ static Listener_function_table callbacks; \ callbacks.listen_callback = &cl::method ## _callback; \ callbacks.mark_callback = &cl::method ## _mark; \ return Listener (this, &callbacks); \ } \ void \ cl :: method ## _proc params #define DECLARE_LISTENER(name) \ inline void name ## _proc (SCM); \ static void name ## _callback (void *self, SCM ev); \ static void name ## _mark (void *self); \ Listener name () const #endif /* LISTENER_HH */
/* dispatcher.hh -- declare Dispatcher source file of the GNU LilyPond music typesetter (c) 2005 Erik Sandberg <[EMAIL PROTECTED]> */ #ifndef DISPATCHER_HH #define DISPATCHER_HH #include "listener.hh" #include "stream-event.hh" class Dispatcher { /* Hash table. Each event-class maps to a list of listeners. */ SCM listeners_; /* alist of dispatchers that we listen to. Each entry is a (dist . priority) pair. */ SCM dispatchers_; SCM listen_classes_; DECLARE_LISTENER (dispatch); /* priority counter. Listeners with low priority receive events first. */ int priority_count_; void internal_add_listener (Listener, SCM event_class, int priority); public: Dispatcher (); void broadcast (Stream_event *ev); void add_listener (Listener, SCM event_class); void remove_listener (Listener, SCM event_class); void register_as_listener (Dispatcher *dist); void unregister_as_listener (Dispatcher *dist); protected: DECLARE_SMOBS (Dispatcher,); }; DECLARE_UNSMOB (Dispatcher, dispatcher); #endif // DISPATCHER_HH
/* stream-event.hh -- declare Stream_event source file of the GNU LilyPond music typesetter (c) 2005-2006 Erik Sandberg <[EMAIL PROTECTED]> */ #ifndef STREAM_EVENT_HH #define STREAM_EVENT_HH #include "lily-proto.hh" #include "smobs.hh" #include "prob.hh" class Stream_event { void init (); SCM property_alist_; Input *origin_; public: Stream_event (); Input *origin () const; DECLARE_SCHEME_CALLBACK (undump, (SCM)); DECLARE_SCHEME_CALLBACK (dump, (SCM)); // todo: make Input mandatory. Stream_event (SCM property_alist); Stream_event (Context *c, SCM class_name); Stream_event (Context *c, Input *); Stream_event (Stream_event *ev); SCM internal_get_property (SCM) const; void internal_set_property (SCM prop, SCM val); protected: DECLARE_SMOBS (Stream_event,); }; DECLARE_UNSMOB (Stream_event, stream_event); DECLARE_TYPE_P (Stream_event); #define SEND_EVENT_TO_CONTEXT(ctx, cl, ...) \ { \ Stream_event *_e_ = new Stream_event (ctx, ly_symbol2scm (cl)); \ __VA_ARGS__; \ ctx->event_source ()->distribute (_e_); \ scm_gc_unprotect_object (_e_->self_scm ()); \ } #define EVENT_PROPERTY(prop, val) \ (_e_->set_property (prop, val)) #endif /* STREAM_EVENT_HH */
_______________________________________________ lilypond-devel mailing list lilypond-devel@gnu.org http://lists.gnu.org/mailman/listinfo/lilypond-devel