X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fdispatcher.cc;h=b1b076759f83b8a7e1a655d84054957b84afb4c5;hb=a6a51abfd0195a3cf7d6ea095cf69808852f21ce;hp=94519021abe43ccb4def37ce25fa595e3dd676a9;hpb=2f0c6eb19208485a86d3416db3f3640a1d54752a;p=lilypond.git diff --git a/lily/dispatcher.cc b/lily/dispatcher.cc index 94519021ab..b1b076759f 100644 --- a/lily/dispatcher.cc +++ b/lily/dispatcher.cc @@ -1,7 +1,7 @@ /* This file is part of LilyPond, the GNU music typesetter. - Copyright (C) 2005--2012 Erik Sandberg + Copyright (C) 2005--2015 Erik Sandberg LilyPond is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,12 +20,10 @@ #include "dispatcher.hh" #include "input.hh" #include "international.hh" -#include "ly-smobs.icc" #include "warn.hh" +#include "lily-imports.hh" -IMPLEMENT_SMOBS (Dispatcher); -IMPLEMENT_TYPE_P (Dispatcher, "ly:dispatcher?"); -IMPLEMENT_DEFAULT_EQUAL_P (Dispatcher); +const char * const Dispatcher::type_p_name_ = "ly:dispatcher?"; Dispatcher::~Dispatcher () { @@ -33,7 +31,6 @@ Dispatcher::~Dispatcher () Dispatcher::Dispatcher () { - self_scm_ = SCM_EOL; listeners_ = SCM_EOL; dispatchers_ = SCM_EOL; listen_classes_ = SCM_EOL; @@ -45,21 +42,18 @@ Dispatcher::Dispatcher () } SCM -Dispatcher::mark_smob (SCM sm) +Dispatcher::mark_smob () const { - Dispatcher *me = (Dispatcher *) SCM_CELL_WORD_1 (sm); - scm_gc_mark (me->dispatchers_); - scm_gc_mark (me->listen_classes_); - return me->listeners_; + scm_gc_mark (dispatchers_); + scm_gc_mark (listen_classes_); + return listeners_; } int -Dispatcher::print_smob (SCM s, SCM p, scm_print_state *) +Dispatcher::print_smob (SCM p, scm_print_state *) const { - Dispatcher *me = (Dispatcher *) SCM_CELL_WORD_1 (s); scm_puts ("#alist"), - me->listeners_), p); + scm_write (Lily::hash_table_to_alist (listeners_), p); scm_puts (">", p); return 1; } @@ -71,25 +65,24 @@ Event dispatching: This is done by keeping a priority queue of listener lists, and iteratively send the event to the lowest-priority listener. - An event is never sent twice to listeners with equal priority. + The only case where listeners with equal priority may exist is when + two dispatchers are connected for more than one event type. In that + case, the respective listeners all have the same priority, making + sure that any event is only dispatched at most once for that + combination of dispatchers, even if it matches more than one event + type. */ -IMPLEMENT_LISTENER (Dispatcher, dispatch); void Dispatcher::dispatch (SCM sev) { - Stream_event *ev = unsmob_stream_event (sev); - SCM class_symbol = ev->get_property ("class"); - if (!scm_is_symbol (class_symbol)) - { - warning (_ ("Event class should be a symbol")); - return; - } - - SCM class_list = scm_call_1 (ly_lily_module_constant ("ly:make-event-class"), class_symbol); + Stream_event *ev = unsmob (sev); + SCM class_list = ev->get_property ("class"); if (!scm_is_pair (class_list)) { - ev->origin ()->warning (_f ("Unknown event class %s", ly_symbol2string (class_symbol).c_str ())); + ev->origin ()->warning (_ ("Event class should be a list")); return; } + #if 0 bool sent = false; #endif @@ -102,7 +95,7 @@ Dispatcher::dispatch (SCM sev) listener list, and the lowest priority element is repeatedly extracted and called. - The priority queue is implemented as a bubble-sorted C + The priority queue is implemented as an insertion-sorted C array. Using the stack instead of native Scheme datastructures avoids overheads for memory allocation. The queue is usually small (around 2 elements), so the quadratic sorting time is not a @@ -121,7 +114,7 @@ Dispatcher::dispatch (SCM sev) num_classes--; else { - // bubblesort. + // insertion sort. int prio = scm_to_int (scm_caar (list)); int j; for (j = i; j > 0 && lists[j - 1].prio > prio; j--) @@ -149,8 +142,8 @@ Dispatcher::dispatch (SCM sev) 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 ()); + SCM l = scm_cdar (lists[0].list); + scm_call_1 (l, ev->self_scm ()); #if 0 sent = true; #endif @@ -173,22 +166,68 @@ Dispatcher::dispatch (SCM sev) #endif } +bool +Dispatcher::is_listened_class (SCM cl) +{ + for (; scm_is_pair (cl); cl = scm_cdr (cl)) + { + SCM list = scm_hashq_ref (listeners_, scm_car (cl), SCM_EOL); + if (scm_is_pair (list)) + return true; + } + return false; +} + +static SCM +accumulate_types (void * /* closure */, + SCM key, + SCM val, + SCM result) +{ + if (scm_is_pair (val)) + return scm_cons (key, result); + return result; +} + +SCM +Dispatcher::listened_types () +{ + return scm_internal_hash_fold ((scm_t_hash_fold_fn) &accumulate_types, + NULL, SCM_EOL, listeners_); +} + void Dispatcher::broadcast (Stream_event *ev) { dispatch (ev->self_scm ()); } +// add_listener will always assign a new priority for each call void Dispatcher::add_listener (Listener l, SCM ev_class) { - internal_add_listener (l, ev_class, ++priority_count_); + add_listener (l.smobbed_copy (), ev_class); +} + +void +Dispatcher::add_listener (SCM callback, SCM ev_class) +{ + internal_add_listener (callback, ev_class, ++priority_count_); } inline void -Dispatcher::internal_add_listener (Listener l, SCM ev_class, int priority) +Dispatcher::internal_add_listener (SCM callback, SCM ev_class, int priority) { - SCM list = scm_hashq_ref (listeners_, ev_class, SCM_EOL); + SCM handle = scm_hashq_create_handle_x (listeners_, ev_class, SCM_EOL); + SCM list = scm_cdr (handle); + // if ev_class is not yet listened to, we go through our list of + // source dispatchers and register ourselves there with the priority + // we have reserved for this dispatcher. The priority system + // usually distributes events in the order events are registered. + // The reuse of a previous priority when registering another event + // for a dispatcher/dispatcher connection bypasses the normal + // ordering, but it is the mechanism by which duplicate broadcasts + // of the same event from one dispatcher to another are avoided. if (!scm_is_pair (list)) { /* Tell all dispatchers that we listen to, that we want to hear ev_class @@ -196,34 +235,36 @@ Dispatcher::internal_add_listener (Listener l, SCM ev_class, int priority) 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 (GET_LISTENER (dispatch), ev_class, priority); + Dispatcher *d = unsmob (scm_caar (disp)); + d->internal_add_listener (GET_LISTENER (Dispatcher, dispatch).smobbed_copy (), + ev_class, priority); } listen_classes_ = scm_cons (ev_class, listen_classes_); } - SCM entry = scm_cons (scm_from_int (priority), l.smobbed_copy ()); - list = scm_merge (list, scm_list_1 (entry), ly_lily_module_constant ("car<")); - scm_hashq_set_x (listeners_, ev_class, list); + SCM entry = scm_cons (scm_from_int (priority), callback); + list = scm_merge (list, scm_list_1 (entry), Lily::car_less); + scm_set_cdr_x (handle, list); } void Dispatcher::remove_listener (Listener l, SCM ev_class) { - SCM list = scm_hashq_ref (listeners_, ev_class, SCM_EOL); + SCM handle = scm_hashq_get_handle (listeners_, ev_class); - if (list == SCM_EOL) + if (scm_is_false (handle)) { programming_error ("remove_listener called with incorrect class."); return; } + SCM list = scm_cdr (handle); // 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) + if (*unsmob (scm_cdadr (e)) == l && first) { scm_set_cdr_x (e, scm_cddr (e)); first = false; @@ -232,17 +273,17 @@ Dispatcher::remove_listener (Listener l, SCM ev_class) else e = scm_cdr (e); list = scm_cdr (dummy); - scm_hashq_set_x (listeners_, ev_class, list); + scm_set_cdr_x (handle, list); if (first) - warning ("Attempting to remove nonexisting listener."); + warning (_ ("Attempting to remove nonexisting listener.")); else if (!scm_is_pair (list)) { /* Unregister with all dispatchers. */ for (SCM disp = dispatchers_; scm_is_pair (disp); disp = scm_cdr (disp)) { - Dispatcher *d = unsmob_dispatcher (scm_caar (disp)); - d->remove_listener (GET_LISTENER (dispatch), ev_class); + Dispatcher *d = unsmob (scm_caar (disp)); + d->remove_listener (GET_LISTENER (Dispatcher, dispatch), ev_class); } listen_classes_ = scm_delq_x (ev_class, listen_classes_); } @@ -252,18 +293,22 @@ Dispatcher::remove_listener (Listener l, SCM ev_class) void Dispatcher::register_as_listener (Dispatcher *disp) { + // We are creating and remembering the priority _we_ have with the + // foreign dispatcher. All events are dispatched with the same + // priority. The result is that, for example, a single event class + // will only trigger an event listener once. int priority = ++disp->priority_count_; // Don't register twice to the same dispatcher. - if (scm_assq (disp->self_scm (), dispatchers_) != SCM_BOOL_F) + if (scm_is_true (scm_assq (disp->self_scm (), dispatchers_))) { - warning ("Already listening to dispatcher, ignoring request"); + warning (_ ("Already listening to dispatcher, ignoring request")); return; } dispatchers_ = scm_acons (disp->self_scm (), scm_from_int (priority), dispatchers_); - Listener list = GET_LISTENER (dispatch); + SCM list = GET_LISTENER (Dispatcher, dispatch).smobbed_copy (); for (SCM cl = listen_classes_; scm_is_pair (cl); cl = scm_cdr (cl)) { disp->internal_add_listener (list, scm_car (cl), priority); @@ -276,7 +321,7 @@ Dispatcher::unregister_as_listener (Dispatcher *disp) { dispatchers_ = scm_assq_remove_x (dispatchers_, disp->self_scm ()); - Listener listener = GET_LISTENER (dispatch); + Listener listener = GET_LISTENER (Dispatcher, dispatch); for (SCM cl = listen_classes_; scm_is_pair (cl); cl = scm_cdr (cl)) { disp->remove_listener (listener, scm_car (cl));