X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fdispatcher.cc;h=49536a0a8a36d7485bb1211522d22e34813591a8;hb=5bbfc22fce036b9b69df5e420de93e11da23c05e;hp=6107594900b678e8a63d2c140ebfe1b3c3176300;hpb=9e69cb84d6ee5b0a861cd97869b10e3bdf0c833c;p=lilypond.git diff --git a/lily/dispatcher.cc b/lily/dispatcher.cc index 6107594900..49536a0a8a 100644 --- a/lily/dispatcher.cc +++ b/lily/dispatcher.cc @@ -1,9 +1,20 @@ /* - dispatcher.cc -- implement Dispatcher + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter + Copyright (C) 2005--2014 Erik Sandberg - (c) 2005-2006 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . */ #include "dispatcher.hh" @@ -13,7 +24,7 @@ #include "warn.hh" IMPLEMENT_SMOBS (Dispatcher); -IMPLEMENT_TYPE_P (Dispatcher, "dispatcher"); +IMPLEMENT_TYPE_P (Dispatcher, "ly:dispatcher?"); IMPLEMENT_DEFAULT_EQUAL_P (Dispatcher); Dispatcher::~Dispatcher () @@ -43,11 +54,12 @@ Dispatcher::mark_smob (SCM sm) } int -Dispatcher::print_smob (SCM s, SCM p, scm_print_state*) +Dispatcher::print_smob (SCM s, SCM p, scm_print_state *) { Dispatcher *me = (Dispatcher *) SCM_CELL_WORD_1 (s); scm_puts ("#listeners_), p); + scm_write (scm_call_1 (ly_lily_module_constant ("hash-table->alist"), + me->listeners_), p); scm_puts (">", p); return 1; } @@ -59,26 +71,28 @@ 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_symbol_p (class_symbol)) - { - warning (_f ("Event class should be a symbol")); - return; - } - - SCM class_list = scm_call_1 (ly_lily_module_constant ("ly:make-event-class"), class_symbol); + 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 int num_classes = scm_ilength (class_list); /* @@ -88,7 +102,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 @@ -98,23 +112,23 @@ Dispatcher::dispatch (SCM sev) The first step is to collect all listener lists and to initially insert them in the priority queue. */ - struct { int prio; SCM list; } lists[num_classes+1]; + 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)) + 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--; + if (!scm_is_pair (list)) + 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--) - lists[j] = lists[j-1]; - lists[j].prio = prio; - lists[j].list = list; - i++; + 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; @@ -135,25 +149,58 @@ Dispatcher::dispatch (SCM sev) assert (lists[0].prio > last_priority); last_priority = lists[0].prio; - Listener *l = unsmob_listener (scm_cdar (lists[0].list)); + Listener *l = Listener::unsmob (scm_cdar (lists[0].list)); l->listen (ev->self_scm ()); +#if 0 sent = true; +#endif } // go to the next listener; bubble-sort the class list. SCM next = scm_cdr (lists[0].list); - if (!scm_is_pair(next)) + 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]; + 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; } -/* TODO: Uncomment. +#if 0 + /* TODO: Uncomment. */ if (!sent) warning (_f ("Junking event: %s", ly_symbol2string (class_symbol).c_str ())); -*/ +#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 @@ -162,6 +209,7 @@ 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) { @@ -172,19 +220,27 @@ inline void Dispatcher::internal_add_listener (Listener l, SCM ev_class, int priority) { SCM list = scm_hashq_ref (listeners_, ev_class, SCM_EOL); + // 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 + /* Tell all dispatchers that we listen to, that we want to hear ev_class events */ - 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); - } + for (SCM disp = dispatchers_; scm_is_pair (disp); disp = scm_cdr (disp)) + { + int priority = scm_to_int (scm_cdar (disp)); + Dispatcher *d = Dispatcher::unsmob (scm_caar (disp)); + d->internal_add_listener (GET_LISTENER (dispatch), ev_class, priority); + } listen_classes_ = scm_cons (ev_class, listen_classes_); } - SCM entry = scm_cons (scm_int2num (priority), l.smobbed_copy ()); + 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); } @@ -205,12 +261,12 @@ Dispatcher::remove_listener (Listener l, SCM ev_class) 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) + while (scm_is_pair (scm_cdr (e))) + if (*Listener::unsmob (scm_cdadr (e)) == l && first) { - scm_set_cdr_x (e, scm_cddr(e)); - first = false; - break; + scm_set_cdr_x (e, scm_cddr (e)); + first = false; + break; } else e = scm_cdr (e); @@ -218,15 +274,15 @@ Dispatcher::remove_listener (Listener l, SCM ev_class) scm_hashq_set_x (listeners_, ev_class, 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 = Dispatcher::unsmob (scm_caar (disp)); + d->remove_listener (GET_LISTENER (dispatch), ev_class); + } listen_classes_ = scm_delq_x (ev_class, listen_classes_); } } @@ -235,16 +291,20 @@ 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) { - warning ("Already listening to dispatcher, ignoring request"); + warning (_ ("Already listening to dispatcher, ignoring request")); return; } - dispatchers_ = scm_acons (disp->self_scm (), scm_int2num (priority), dispatchers_); + dispatchers_ = scm_acons (disp->self_scm (), scm_from_int (priority), dispatchers_); Listener list = GET_LISTENER (dispatch); for (SCM cl = listen_classes_; scm_is_pair (cl); cl = scm_cdr (cl))