]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/dispatcher.cc
Web-ja: update introduction
[lilypond.git] / lily / dispatcher.cc
index 6a11654e233465ea6a59de9947b8c28aba33f3f4..b1b076759f83b8a7e1a655d84054957b84afb4c5 100644 (file)
@@ -1,7 +1,7 @@
 /*
   This file is part of LilyPond, the GNU music typesetter.
 
-  Copyright (C) 2005--2011 Erik Sandberg  <mandolaerik@gmail.com>
+  Copyright (C) 2005--2015 Erik Sandberg  <mandolaerik@gmail.com>
 
   LilyPond is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #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,20 +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 ("#<Dispatcher ", p);
-  scm_write (scm_vector_to_list (me->listeners_), p);
+  scm_write (Lily::hash_table_to_alist (listeners_), p);
   scm_puts (">", p);
   return 1;
 }
@@ -70,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<Stream_event> (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
@@ -101,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
@@ -120,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--)
@@ -148,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
@@ -167,27 +161,73 @@ Dispatcher::dispatch (SCM sev)
 
 #if 0
   /* TODO: Uncomment. */
-    if (!sent)
-      warning (_f ("Junking event: %s", ly_symbol2string (class_symbol).c_str ()));
+  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
 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
@@ -195,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<Dispatcher> (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<Listener> (scm_cdadr (e)) == l && first)
       {
         scm_set_cdr_x (e, scm_cddr (e));
         first = false;
@@ -231,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<Dispatcher> (scm_caar (disp));
+          d->remove_listener (GET_LISTENER (Dispatcher, dispatch), ev_class);
         }
       listen_classes_ = scm_delq_x (ev_class, listen_classes_);
     }
@@ -251,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);
@@ -275,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));