eventresponsehdlr.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 1999-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Tue Nov  9 15:32:48 1999
00010 // commit: $Id: eventresponsehdlr.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/visx/eventresponsehdlr.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_VISX_EVENTRESPONSEHDLR_CC_UTC20050626084016_DEFINED
00035 #define GROOVX_VISX_EVENTRESPONSEHDLR_CC_UTC20050626084016_DEFINED
00036 
00037 #include "visx/eventresponsehdlr.h"
00038 
00039 #include "io/reader.h"
00040 #include "io/writer.h"
00041 
00042 #include "nub/log.h"
00043 #include "nub/ref.h"
00044 
00045 #include "tcl/makecmd.h"
00046 #include "tcl/eventloop.h"
00047 #include "tcl/interp.h"
00048 
00049 #include "tcl-gfx/toglet.h"
00050 #include "tcl-io/tclprocwrapper.h"
00051 
00052 #include "rutz/fileposition.h"
00053 #include "rutz/fstring.h"
00054 #include "rutz/shared_ptr.h"
00055 #include "rutz/scopedptr.h"
00056 #include "rutz/sfmt.h"
00057 
00058 #include "visx/feedbackmap.h"
00059 #include "visx/sound.h"
00060 #include "visx/response.h"
00061 #include "visx/trial.h"
00062 
00063 #define GVX_DYNAMIC_TRACE_EXPR EventResponseHdlr::tracer.status()
00064 #include "rutz/trace.h"
00065 #include "rutz/debug.h"
00066 GVX_DBG_REGISTER
00067 
00068 using rutz::fstring;
00069 
00071 //
00072 // File scope data
00073 //
00075 
00076 namespace
00077 {
00078   const io::version_id ERH_SVID = 2;
00079 
00080   fstring uniqCmdName(const char* stem)
00081   {
00082     static int cmdCounter = 0;
00083 
00084     return rutz::sfmt("::__ERHPrivate::%s%d", stem, ++cmdCounter);
00085   }
00086 }
00087 
00089 //
00090 // EventResponseHdlr::Impl class definition
00091 //
00093 
00094 class EventResponseHdlr::Impl
00095 {
00096 private:
00097   Impl(const Impl&);
00098   Impl& operator=(const Impl&);
00099 
00100 public:
00101   Impl(EventResponseHdlr* owner);
00102   ~Impl();
00103 
00104   class ActiveState;
00105   friend class ActiveState;
00106 
00107   class ActiveState
00108   {
00109   private:
00110     nub::soft_ref<Toglet> itsWidget;
00111     Trial& itsTrial;
00112     fstring itsEventSequence;
00113     fstring itsBindingScript;
00114     unsigned int itsResponseCount;
00115 
00116     void attend()
00117     {
00118       if (itsWidget.is_valid())
00119         itsWidget->bind(itsEventSequence.c_str(), itsBindingScript.c_str());
00120     }
00121 
00122     void ignore()
00123     {
00124       if (itsWidget.is_valid())
00125         itsWidget->bind(itsEventSequence.c_str(), "");
00126     }
00127 
00128   public:
00129     ~ActiveState() { ignore(); }
00130 
00131     ActiveState(const EventResponseHdlr::Impl* erh,
00132                 nub::soft_ref<Toglet> widget, Trial& trial,
00133                 const fstring& seq, const fstring& script) :
00134       itsWidget(widget),
00135       itsTrial(trial),
00136       itsEventSequence(seq),
00137       itsBindingScript(script),
00138       itsResponseCount(0)
00139     {
00140       GVX_PRECONDITION((erh != 0) && (widget.is_valid()) && (&trial != 0));
00141       attend();
00142     }
00143 
00144     void rhAbortTrial()
00145     {
00146       ignore();
00147 
00148       nub::ref<Sound> p = Sound::getErrSound();
00149       p->play();
00150     }
00151 
00152     void rhEndTrial() { ignore(); }
00153 
00154     void rhHaltExpt() { ignore(); }
00155 
00156     void handleResponse(EventResponseHdlr::Impl* rep, const char* event_info)
00157     {
00158       Response theResponse;
00159 
00160       theResponse.setMsec(int(itsTrial.trElapsedMsec() + 0.5));
00161 
00162       nub::log( rutz::sfmt("event_info: %s", event_info) );
00163 
00164       theResponse.setVal(Response::INVALID_VALUE);
00165 
00166       if ( !rep->itsResponseProc->isNoop() )
00167         {
00168           try {
00169             theResponse.setVal(rep->itsResponseProc->call<int>(event_info));
00170           } catch (...) {}
00171         }
00172 
00173       nub::log( rutz::sfmt("response val: %d", theResponse.val()) );
00174 
00175       if (theResponse.shouldIgnore())
00176         return;
00177 
00178       if (++itsResponseCount >= rep->itsMaxResponses)
00179         ignore();
00180 
00181       if ( !theResponse.is_valid() )
00182         {
00183           if ( rep->itsAbortInvalidResponses )
00184             itsTrial.trAbortTrial();
00185         }
00186       else
00187         {
00188           itsTrial.trProcessResponse(theResponse);
00189           rep->itsFeedbackMap.giveFeedback(rep->itsInterp, theResponse.val());
00190         }
00191     }
00192   };
00193 
00194   void becomeActive(nub::soft_ref<Toglet> widget, Trial& trial) const
00195   {
00196     nub::log( rutz::sfmt("binding to %s", itsCallbackName.c_str()) );
00197 
00198     const fstring script =
00199       rutz::sfmt("%s %lu [list %s]",
00200                  itsCallbackName.c_str(), itsOwner->id(),
00201                  itsBindingSubstitution.c_str());
00202 
00203     itsState.reset(new ActiveState(this, widget, trial,
00204                                    itsEventSequence, script));
00205   }
00206 
00207   void becomeInactive() const { itsState.reset(0); }
00208 
00209   bool isActive() const   { return itsState.get() != 0; }
00210   bool isInactive() const { return itsState.get() == 0; }
00211 
00212   static void handleResponseCallback(nub::ref<EventResponseHdlr> erh,
00213                                      const char* event_info)
00214   {
00215     EventResponseHdlr::Impl* rep = erh->rep;
00216     GVX_PRECONDITION( rep->isActive() );
00217 
00218     rep->itsState->handleResponse(rep, event_info);
00219   }
00220 
00221   //
00222   // data
00223   //
00224 
00225   EventResponseHdlr* itsOwner;
00226 
00227   mutable rutz::scoped_ptr<ActiveState> itsState;
00228 
00229   tcl::interpreter itsInterp;
00230 
00231   fstring itsCallbackName;
00232 
00233   FeedbackMap itsFeedbackMap;
00234 
00235   fstring itsEventSequence;
00236   fstring itsBindingSubstitution;
00237 
00238   bool itsAbortInvalidResponses;
00239 
00240   nub::ref<tcl::ProcWrapper> itsResponseProc;
00241 
00242   unsigned int itsMaxResponses;
00243 };
00244 
00246 //
00247 // Static member definitions
00248 //
00250 
00251 rutz::tracer EventResponseHdlr::tracer;
00252 
00253 
00255 //
00256 // EventResponseHdlr::Impl method definitions
00257 //
00259 
00260 EventResponseHdlr::Impl::Impl(EventResponseHdlr* owner) :
00261   itsOwner(owner),
00262   itsState(0),
00263   itsInterp(tcl::event_loop::interp()),
00264   itsCallbackName(uniqCmdName("handler")),
00265   itsFeedbackMap(),
00266   itsEventSequence("<KeyPress>"),
00267   itsBindingSubstitution("%K"),
00268   itsAbortInvalidResponses(true),
00269   itsResponseProc(new tcl::ProcWrapper(itsInterp, uniqCmdName("responseProc"))),
00270   itsMaxResponses(1)
00271 {
00272 GVX_TRACE("EventResponseHdlr::Impl::Impl");
00273 
00274   tcl::make_command(itsInterp, &handleResponseCallback,
00275                     itsCallbackName.c_str(), "<private>", SRC_POS);
00276 }
00277 
00278 EventResponseHdlr::Impl::~Impl()
00279 {
00280 GVX_TRACE("EventResponseHdlr::Impl::~Impl");
00281 
00282   // force the destructor to run now, rather than after ~Impl()
00283   // finishes, so that the reference to *this in ActiveState does
00284   // not become invalid
00285   itsState.reset(0);
00286 }
00287 
00288 
00290 //
00291 // EventResponseHdlr method definitions
00292 //
00294 
00295 EventResponseHdlr* EventResponseHdlr::make()
00296 {
00297 GVX_TRACE("EventResponseHdlr::make");
00298   return new EventResponseHdlr;
00299 }
00300 
00301 EventResponseHdlr::EventResponseHdlr() :
00302   ResponseHandler(),
00303   rep(new Impl(this))
00304 {}
00305 
00306 EventResponseHdlr::~EventResponseHdlr() throw()
00307   { delete rep; }
00308 
00309 io::version_id EventResponseHdlr::class_version_id() const
00310   { return ERH_SVID; }
00311 
00312 void EventResponseHdlr::read_from(io::reader& reader)
00313 {
00314 GVX_TRACE("EventResponseHdlr::read_from");
00315 
00316   reader.ensure_version_id("EventResponseHdlr", 2,
00317                            "Try cvs tag xml_conversion_20040526",
00318                            SRC_POS);
00319 
00320   rep->becomeInactive();
00321 
00322   fstring fmap;
00323   reader.read_value("feedbackMap", fmap);
00324   rep->itsFeedbackMap.set(fmap);
00325 
00326   reader.read_value("useFeedback", rep->itsFeedbackMap.itsUseFeedback);
00327   reader.read_value("eventSequence", rep->itsEventSequence);
00328   reader.read_value("bindingSubstitution", rep->itsBindingSubstitution);
00329 
00330   reader.read_owned_object("responseProc", rep->itsResponseProc);
00331 }
00332 
00333 void EventResponseHdlr::write_to(io::writer& writer) const
00334 {
00335 GVX_TRACE("EventResponseHdlr::write_to");
00336 
00337   writer.ensure_output_version_id("EventResponseHdlr", ERH_SVID, 2,
00338                               "Try groovx0.8a7", SRC_POS);
00339 
00340   writer.write_value("feedbackMap", rep->itsFeedbackMap.rep());
00341   writer.write_value("useFeedback", rep->itsFeedbackMap.itsUseFeedback);
00342   writer.write_value("eventSequence", rep->itsEventSequence);
00343   writer.write_value("bindingSubstitution", rep->itsBindingSubstitution);
00344 
00345   writer.write_owned_object("responseProc", rep->itsResponseProc);
00346 }
00347 
00348 // FIXME XML still need this?
00349 void EventResponseHdlr::setInputResponseMap(const fstring& s)
00350 {
00351 GVX_TRACE("EventResponseHdlr::setInputResponseMap");
00352 
00353   const fstring args("inp");
00354 
00355   const fstring body =
00356     rutz::sfmt("set map {%s}\n"
00357                "foreach pair $map {\n"
00358                "  foreach {rx val} $pair {\n"
00359                "    if [regexp $rx $inp] { return $val }\n"
00360                "  }\n"
00361                "}\n"
00362                "return -1\n",
00363                s.c_str());
00364 
00365   setResponseProc(args, body);
00366 }
00367 
00368 bool EventResponseHdlr::getUseFeedback() const
00369 {
00370 GVX_TRACE("EventResponseHdlr::getUseFeedback");
00371   return rep->itsFeedbackMap.itsUseFeedback;
00372 }
00373 
00374 void EventResponseHdlr::setUseFeedback(bool val)
00375 {
00376 GVX_TRACE("EventResponseHdlr::setUseFeedback");
00377   rep->itsFeedbackMap.itsUseFeedback = val;
00378 }
00379 
00380 const char* EventResponseHdlr::getFeedbackMap() const
00381 {
00382 GVX_TRACE("EventResponseHdlr::getFeedbackMap");
00383   return rep->itsFeedbackMap.rep().c_str();
00384 }
00385 
00386 void EventResponseHdlr::setFeedbackMap(const char* feedback_string)
00387 {
00388 GVX_TRACE("EventResponseHdlr::setFeedbackMap");
00389   rep->itsFeedbackMap.set(feedback_string);
00390 }
00391 
00392 const fstring& EventResponseHdlr::getEventSequence() const
00393   { return rep->itsEventSequence; }
00394 
00395 void EventResponseHdlr::setEventSequence(const fstring& seq)
00396   { rep->itsEventSequence = seq; }
00397 
00398 const fstring& EventResponseHdlr::getBindingSubstitution() const
00399   { return rep->itsBindingSubstitution; }
00400 
00401 void EventResponseHdlr::setBindingSubstitution(const fstring& sub)
00402   { rep->itsBindingSubstitution = sub; }
00403 
00404 fstring EventResponseHdlr::getResponseProc() const
00405 {
00406   return rep->itsResponseProc->fullSpec();
00407 }
00408 
00409 void EventResponseHdlr::setResponseProc(const fstring& args,
00410                                         const fstring& body)
00411 {
00412   rep->itsResponseProc->define(args, body);
00413 }
00414 
00415 void EventResponseHdlr::abortInvalidResponses()
00416   { rep->itsAbortInvalidResponses = true; }
00417 
00418 void EventResponseHdlr::ignoreInvalidResponses()
00419   { rep->itsAbortInvalidResponses = false; }
00420 
00421 void EventResponseHdlr::setMaxResponses(unsigned int count)
00422   { rep->itsMaxResponses = count; }
00423 
00424 unsigned int EventResponseHdlr::getMaxResponses() const
00425   { return rep->itsMaxResponses; }
00426 
00427 void EventResponseHdlr::rhBeginTrial(nub::soft_ref<Toglet> widget,
00428                                      Trial& trial) const
00429 {
00430   GVX_PRECONDITION( rep->isInactive() );
00431 
00432   rep->itsInterp.clear_event_queue();
00433 
00434   rep->becomeActive(widget, trial);
00435 
00436   GVX_POSTCONDITION( rep->isActive() );
00437 }
00438 
00439 void EventResponseHdlr::rhAbortTrial() const
00440 {
00441   if ( rep->isActive() )
00442     rep->itsState->rhAbortTrial();
00443 }
00444 
00445 void EventResponseHdlr::rhEndTrial() const
00446 {
00447   if ( rep->isActive() )
00448     {
00449       rep->itsState->rhEndTrial();
00450       rep->becomeInactive();
00451     }
00452 
00453   GVX_POSTCONDITION( rep->isInactive() );
00454 }
00455 
00456 void EventResponseHdlr::rhHaltExpt() const
00457 {
00458   if ( rep->isActive() )
00459     {
00460       rep->itsState->rhHaltExpt();
00461       rep->becomeInactive();
00462     }
00463 
00464   GVX_POSTCONDITION( rep->isInactive() );
00465 }
00466 
00467 void EventResponseHdlr::rhAllowResponses(nub::soft_ref<Toglet> widget,
00468                                          Trial& trial) const
00469 {
00470   rep->becomeActive(widget, trial);
00471 
00472   GVX_POSTCONDITION( rep->isActive() );
00473 }
00474 
00475 void EventResponseHdlr::rhDenyResponses() const
00476 {
00477   rep->becomeInactive();
00478 
00479   GVX_POSTCONDITION( rep->isInactive() );
00480 }
00481 
00482 static const char __attribute__((used)) vcid_groovx_visx_eventresponsehdlr_cc_utc20050626084016[] = "$Id: eventresponsehdlr.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00483 #endif // !GROOVX_VISX_EVENTRESPONSEHDLR_CC_UTC20050626084016_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:41 2008 by Doxygen version 1.5.5.