PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PEventLoop.cpp
Go to the documentation of this file.
1//* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
3// Name: src/devices/PEventLoop.cpp
4// Purpose: Primary generic timer event device
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2003-2026 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9//
10//
11// This file is part of the PEBL project.
12//
13// PEBL is free software; you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation; either version 2 of the License, or
16// (at your option) any later version.
17//
18// PEBL is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with PEBL; if not, write to the Free Software
25// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27#include "PEventLoop.h"
28#include "PDevice.h"
29#include "PEventQueue.h"
30#ifdef PEBL_VALIDATOR
31#include "../platforms/validator/PlatformEventQueue.h"
32#else
33#include "../platforms/sdl/PlatformEventQueue.h"
34#endif
35#include "../base/FunctionMap.h"
36
37#include "../apps/Globals.h"
38
39#ifdef PEBL_ITERATIVE_EVAL
40#include "PEventLoop-es.h"
41#include "../base/Evaluator-es.h"
42#else
43#include "PEventLoop.h"
44#include "../base/Evaluator.h"
45#endif
46
47#ifdef PEBL_EMSCRIPTEN
48#include "emscripten.h"
49#endif
50
51
52#include "../base/PComplexData.h"
53#include "../base/PEBLObject.h"
54#include "../base/grammar.tab.hpp"
55
56#include "../utility/Defs.h"
57
58#include "../libs/PEBLEnvironment.h"
59#include <iostream>
60
61using std::flush;
62using std::endl;
63
65
68{
69 //cout << "Creating event loop\n";
70
71}
72
75{
76 // Standard Destructor
77 //cout << "Destroying venet loop\n";
78}
79
80
81
87 const std::string & function,
88 Variant parameters)
89{
90
91 //Add the state to the states list.
92 mStates.push_back(state);
93 //std::cerr << "[DEBUG] RegisterState: " << mStates.size() << " state(s) registered" << std::endl;
94 //Make a PNode representing the function. If the function-name is null, push a
95 //null PNode onto the vector; this is a short-cut for an end-of-loop event.
96 if(function != "")
97 {
98
99 //Get the node associated with the function name.
100 //Note that node will be the right node of a PEBL_FUNCTION node
101 //The namenode goes on the left.
102 Variant fname = Variant(function.c_str(),P_DATA_FUNCTION);
103 DataNode * namenode = new DataNode(fname,"user-generated function",-1);
104
105 //On the right, we need a node representing the lambda function.
106
107 //we need the arglist too.
108
109 PNode * node = Evaluator::mFunctionMap.GetFunction(function);
110
111 PNode * arglist = ((OpNode*)node)->GetLeft();
112
113 PNode * fnode = new OpNode(PEBL_FUNCTION, namenode, arglist, "user-generated", -1);
114
115
116 mNodes.push_back(fnode);
117
118
119 }
120 else
121 {
122
123 mNodes.push_back(NULL);
124 }
125 //Add parameters, for use later.
126 //parameters is passed in as a pointer, and must be attached to a counted pointer
127 //if we are to maintain it once the original function is gone.
128
129
130 mParameters.push_back(parameters);
131 mIsEvent.push_back(false);
132}
133
138
141void PEventLoop::RegisterEvent(DeviceState * state, const std::string & function, Variant parameters)
142{
143
144
145 //Add the state to the states list.
146 mStates.push_back(state);
147 //std::cerr << "[DEBUG] RegisterEvent: " << mStates.size() << " event(s) registered" << std::endl;
148 //Make a PNode representing the function. If the function-name is null, push a
149 //null PNode onto the vector; this is a short-cut for an end-of-loop event.
150 if(function != "")
151 {
152
153 //Get the node associated with the function name.
154 //Note that node will be the right node of a PEBL_FUNCTION node
155 //The namenode goes on the left.
156 Variant fname = Variant(function.c_str(),P_DATA_FUNCTION);
157 DataNode * namenode = new DataNode(fname,"user-generated function",-1);
158
159 //On the right, we need a node representing the lambda function.
160
161 PNode * node = Evaluator::mFunctionMap.GetFunction(function);
162
163 PNode * arglist = ((OpNode*)node)->GetLeft();
164
165 PNode * fnode = new OpNode(PEBL_FUNCTION, namenode, arglist, "user-generated", -1);
166
167
168
169 mNodes.push_back(fnode);
170 }
171 else
172 {
173 mNodes.push_back(NULL);
174 }
175 //Add parameters, for use later.
176
177 mParameters.push_back(parameters);
178 mIsEvent.push_back(true);
179}
180
181
182
184{
185
186 //When the eventloop is Clear()ed, the states should be deleted one-by-one,
187 //to avoid a memory leak.
188 std::vector<DeviceState*>::iterator i = mStates.begin();
189 while(i != mStates.end())
190 {
191 if(*i) delete *i;
192 i++;
193 }
194
195 mStates.clear();
196 mNodes.clear();
197 mParameters.clear();
198 mIsEvent.clear();
199
200}
201
202
207
208
210{
211
212 Evaluator * myEval = new Evaluator();
213 unsigned int i, result =0;
214 PEvent returnval(PDT_UNKNOWN,0,0);
215
216 //Enter a variable into the global variable map. The loop will exit
217 //when this is set false.
218 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 1);
219
220 // cout <<"*****" <<myEval->gGlobalVariableMap.RetrieveValue("gKeepLooping") << "--"<< mStates.size() << std::endl;
221
222 //while loop stops when gKeepLooping turns false or there are no more states to check for.
223 // bool stop = (mStates.size()==0) ||
224 // ((pInt)(myEval->gGlobalVariableMap.RetrieveValue("gKeepLooping"))==(pInt)0);
225
226
227 while(myEval->gGlobalVariableMap.RetrieveValue("gKeepLooping"))
228 {
229 //Output time for each event loop cycle.
230 //std::cerr << "time:"<< SDL_GetTicks() << std::endl;
231
232 //At the beginning of a cycle, the event queue has not yet been primed.
234
235
236 //Scan through each test in the mstates vector
237 for(i = 0; i < mStates.size(); i++)
238 {
239 //cout << i << "/"<<mStates.size() << ":"<< mNodes[i] << "\n";
240
241 if(mIsEvent[i]) //The test is for an event queue-type event.
242 {
243
244
245 // Note: 'events' contrast with 'states', handled later.
246 // These are devices which send events through the PEBL Event queue.
247 // So, if the current test is an 'event' state, we need to check the event queue.
248
249
250 //Only test the event if the queue is not empty.
251 if(!gEventQueue->IsEmpty())
252 {
253
254 //Now, we only should test an event if it is the proper device type.
255 //cout << "state/devicetype ["<<i<<"]";
256 //cout << mStates[i]->GetDeviceType() << "---" << std::flush;
257 //cout <<*(mStates[i]) << endl;
258
259 //cout << "Event: " << PDT_WINDOW_RESIZE <<"|"<< gEventQueue->GetFirstEventType() << endl;
260 //cout << gEventQueue->GetFirstEventType() << "<>" << mStates[i]->GetDeviceType() <<endl;
261
262 if(gEventQueue->GetFirstEventType() == mStates[i]->GetDeviceType())
263 {
264
265
266 //Now, just test the device.
267 result = mStates[i]->TestDevice();
268 //cout << "result:" << result << endl;
269
270 if(result)
271 {
272 returnval = gEventQueue->GetFirstEvent();
273
274
275 // The test was successful.
276
277 if(mNodes[i]) //Execute mNodes
278 {
279 //Add the parameters, as a list, to the stack.
280
281 //need to add the variant-based returnval
282 //to the parameters though. It should be at the END
283
284 Variant parlist = mParameters[i];
285
286 //We need to make a deep copy here,tsktsktsk.
287
288 const PList *tmp = parlist.GetComplexData()->GetList();
289 PList * list = new PList(*tmp);
290
291 list->PushBack(Variant(returnval));
293 PComplexData * pcd = new PComplexData(list2);
294
295 myEval->Push(Variant(pcd));
297
298 //The return value is currently unused.
299 // we could use it to remove an event dynamically.
300 Variant outcome = myEval->Pop();
301
302 //cout<< "OUTCOME: " << outcome << endl;
303
304 if(outcome=="<REMOVE>"| outcome=="<remove>")
305 {
306
307 //remove element i from consideration.
308 //it needs to be removed from:
309
310 //mStates[i] (the test)
311 //mNodes[i] (the executed code when test succeeds)
312 //and mParameters[i] (the parameters)
313 //and mIsEvent[] (a flag)
314 //but these are vectors, and not easily removed. We
315 //alsoneed to be wary of removing the last/only state.
316 mStates.erase(mStates.begin()+i);
317 mNodes.erase(mNodes.begin()+i);
318 mParameters.erase(mParameters.begin()+i);
319 mIsEvent.erase(mIsEvent.begin()+i);
320
321 //stop loop in case we have ended.
322 if(mStates.size()<=0)
323 {
324 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 0);
325 }
326 }
327
328 }
329 else //If mNodes[i] is null, terminate
330 {
331
332 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 0);
333
334 }
335 goto end;
336 }
337 }
338 }
339
340 }
341 else
342 {
343 //mStates[i] isn't a device-type state.
344
345 //The test examines the device's state directly.
346 //I don't think any devices support TestDevice currently,
347 //except for keyboard states and timer stats.
348
349 result = mStates[i]->TestDevice();
350 if(result)
351
352 {
353 //We need to create a 'dummy' event to use here.
354 PEBL_DummyEvent pde;
355
356 pde.value = mStates[i]->GetState(mStates[i]->GetInterface());
357
358 if(mStates[i]->GetDevice()->GetDeviceType() == PDT_TIMER)
359 {
360
361 returnval = PEvent(PDT_TIMER,0,0);
362 returnval.SetDummyEvent(pde);
363
364 }
365 else if(mStates[i]->GetDevice()->GetDeviceType()==PDT_KEYBOARD)
366 {
367 //This is a keyboard state; different form a keypress event!
368 //cout << "Keyboard state:" << mStates[i]->GetInterface()<< endl;
369
370 pde.value = (PEBL_Keycode)(mStates[i]->GetInterface());
371
372
373 returnval = PEvent(PDT_DUMMY,0,0);
374 returnval.SetDummyEvent(pde);
375
376 }else
377 {
378
379 //If this was a time-check event, make a PDT_timer
380 //time needs to go in the 0 below.
381 //returnval = PEvent(PDT_DUMMY,0);
382
383
384 returnval = PEvent((PEBL_DEVICE_TYPE)pde.value,0,0);
385 returnval.SetDummyEvent(pde);
386 }
387 //If mNodes[i] is null, terminate
388
389 if(mNodes[i])
390 {
391
392 //Add the parameters, as a list, to the stack.
393
394 //need to add the variant-based returnval
395 //to the parameters though. It should be at the END
396
397 Variant parlist = mParameters[i];
398 //We need to make a deep copy here,
399 //or the appending will stay around until later. tsktsktsk.
400 const PList *tmp = parlist.GetComplexData()->GetList();
401 PList * list = new PList(*tmp);
402
403 list->PushBack(Variant(returnval));
405 PComplexData * pcd = new PComplexData(list2);
406
407 myEval->Push(Variant(pcd));
409
410 //The return value is currently unused.
411 // we could use it to remove an event dynamically.
412 Variant outcome = myEval->Pop();
413
414
415 if(outcome=="<REMOVE>")
416 {
417 //remove element i from consideration,
418 //it needs to be removed from:
419
420 //mStates[i] (the test)
421 //mNodes[i] (the executed code when test succeeds)
422 //and mParameters[i] (the parameters)
423 //and mIsEvent[] (a flag)
424 //but these are vectors, and not easily removed. We
425 //alsoneed to be wary of removing the last/only state.
426 mStates.erase(mStates.begin()+i);
427 mNodes.erase(mNodes.begin()+i);
428 mParameters.erase(mParameters.begin()+i);
429 mIsEvent.erase(mIsEvent.begin()+i);
430
431 //stop loop in case we have ended.
432 if(mStates.size()<=0)
433 {
434 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 0);
435
436 }
437 }
438 }
439 else
440 {
441
442 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 0);
443
444 }
445 goto end;
446
447 }
448 }
449 end:;
450 //cout << std::endl;
451 }
452
453
454 //end:
455 //Get rid of the top item in the event queue
457
458 //Sleep to avoid burning CPU, if the gSleepEasy variable is set.
459 //Uses platform-specific sleep via PlatformTimer (nanosleep on Unix, SDL_Delay on Windows, etc.)
460 if(myEval->gGlobalVariableMap.Exists("gSleepEasy") )
461 {
462 if(myEval->gGlobalVariableMap.RetrieveValue("gSleepEasy"))
463 {
464 PEBLEnvironment::myTimer.Sleep(1); // 1ms sleep
465 }
466 }
467
468 //recompute the stopping criterion
469 if(mStates.size()==0)
470 myEval->gGlobalVariableMap.AddVariable("gKeepLooping",0);
472 }
473
474 delete myEval;
475 return returnval;
476}
477
478
479//Overload of the << operator
480std::ostream & operator <<(std::ostream & out, const PEventLoop & loop )
481{
482
483 out << "PEBL Event Loop: " << flush;
484 loop.Print(out);
485 return out;
486
487}
488
489
490void PEventLoop::Print(std::ostream & out) const
491{
492 out << " ---------------\n";
493 out << "Number of states:" <<mStates.size() << endl;
494
495}
#define NULL
Definition BinReloc.cpp:317
Evaluator * myEval
Definition PEBL.cpp:188
PEBL_DEVICE_TYPE
Definition PDevice.h:40
@ PDT_UNKNOWN
Definition PDevice.h:41
@ PDT_DUMMY
Definition PDevice.h:60
@ PDT_KEYBOARD
Definition PDevice.h:42
@ PDT_TIMER
Definition PDevice.h:48
PlatformEventQueue * gEventQueue
std::ostream & operator<<(std::ostream &out, const PEventLoop &loop)
PlatformEventQueue * gEventQueue
PEBL_Keycode
Definition PKeyboard.h:78
@ P_DATA_FUNCTION
Definition Variant.h:43
This class has got everything you need to evaluate stuff.
void Push(Variant v)
static VariableMap gGlobalVariableMap
Variant Pop()
static FunctionMap mFunctionMap
Initiate some static member data.
void CallFunction(const OpNode *node)
PNode * GetFunction(const std::string &funcname)
PList * GetList() const
std::vector< Variant > mParameters
std::vector< bool > mIsEvent
std::vector< PNode * > mNodes
PEvent Loop()
Initiates the looping tests.
void RegisterEvent(DeviceState *state, const std::string &function, Variant parameters)
void Print(std::ostream &out) const
void RegisterState(DeviceState *state, const std::string &function, Variant parameters)
~PEventLoop()
This is the standard pNode destructor.
PEventLoop()
This is the standard PEventLoop constructor.
std::vector< DeviceState * > mStates
virtual PEBL_DEVICE_TYPE GetFirstEventType()
virtual void PopEvent()
virtual PEvent GetFirstEvent()
virtual bool IsEmpty()
Definition PEventQueue.h:57
Definition PList.h:45
void PushBack(const Variant &v)
Definition PList.cpp:149
Definition PNode.h:45
virtual void Sleep(unsigned long int msecs)
bool Exists(const std::string &varname)
void AddVariable(const std::string &varname, const Variant &val)
Variant RetrieveValue(const std::string &varname)
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
PlatformTimer myTimer