PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PEventLoop-es.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
28#include "PDevice.h"
29#include "PEventQueue.h"
30#include "../platforms/sdl/PlatformEventQueue.h"
31#include "../base/FunctionMap.h"
32
33#include "../apps/Globals.h"
34
35#ifdef PEBL_EMSCRIPTEN
36#include "PEventLoop-es.h"
37//#include "../base/Evaluator-es.h"
38#include "../base/Evaluator.h"
39#include "emscripten.h"
40#else
41#include "PEventLoop.h"
42#include "../base/Evaluator.h"
43#endif
44
45#include "../base/PComplexData.h"
46#include "../base/PEBLObject.h"
47#include "../base/PNode.h"
48#include "../base/grammar.tab.hpp"
49
50#include "../utility/Defs.h"
51#include "../utility/PEBLUtility.h"
52
53#include "../libs/PEBLEnvironment.h"
54#include <iostream>
55
56using std::flush;
57using std::endl;
58
60//extern PEventLoop * eloop;
61
62//namespace PEventLoop
63//{
64// PEventLoop *eloop = NULL;
65//}
66
69{
70
71 char s[25];
72 char* mTmp;
74 mTmp = s;
75 //cout << "CREATING AN EVENT LOOP " << mTmp << endl;
76 // eloop = this;
77 mNumStates = 0;
78 mIsLooping = false;
79 mCallbackScheduled = false;
80 mCallbackNodeStackSize = 0;
81}
82
85{
86 //cout << "Dstructing veentloop " << mTmp << endl;
87 // Standard Destructor
88
89}
90
91
92
98 const std::string & function,
99 Variant parameters)
100{
101
102
103 //Add the state to the states list.
104 mStates.push_back(state);
105 mNumStates++;
106 //Make a PNode representing the function. If the function-name is null, push a
107 //null PNode onto the vector; this is a short-cut for an end-of-loop event.
108 if(function != "")
109 {
110
111 //Store the function name for later use
112 Variant fname = Variant(function.c_str(),P_DATA_FUNCTION);
113 mFunctionNames.push_back(fname);
114
115 //We don't store any node - we'll handle the function call manually
116 mNodes.push_back(NULL);
117
118 }
119 else
120 {
121
122 mNodes.push_back(NULL);
123 mFunctionNames.push_back(Variant(""));
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
142 const std::string & function,
143 Variant parameters)
144{
145
146 // cout << "Regestering...mNumStates:" << mNumStates << endl;
147 // cout << mStates.size() << endl;
148 //Add the state to the states list.
149 mStates.push_back(state);
150 mNumStates++;
151 //Make a PNode representing the function. If the function-name is null, push a
152 //null PNode onto the vector; this is a short-cut for an end-of-loop event.
153 if(function != "")
154 {
155
156 //Store the function name for later use
157 Variant fname = Variant(function.c_str(),P_DATA_FUNCTION);
158 mFunctionNames.push_back(fname);
159
160 //We don't store any node - we'll handle the function call manually
161 mNodes.push_back(NULL);
162 }
163 else
164 {
165 mNodes.push_back(NULL);
166 mFunctionNames.push_back(Variant(""));
167 }
168 //Add parameters, for use later.
169
170 mParameters.push_back(parameters);
171 mIsEvent.push_back(true);
172}
173
174
175
177{
178
179 //When the eventloop is Clear()ed, the states should be deleted one-by-one,
180 //to avoid a memory leak.
181 std::vector<DeviceState*>::iterator i = mStates.begin();
182 while(i != mStates.end())
183 {
184 if(*i) delete *i;
185 i++;
186 }
187
188 mStates.clear();
189 mNodes.clear();
190 mFunctionNames.clear();
191 mParameters.clear();
192 mIsEvent.clear();
193 mNumStates=0;
194 mIsLooping = false;
195 // cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Clearning:\n";
196 // cout << *this << endl;
197}
198
199
204
205// For emscripten, this sets up the loop, then issues the loop command which reprograms itself as needed.
206//
208{
209
210 mIsLooping = true;
211
212 unsigned int i, result =0;
213 PEvent returnval(PDT_UNKNOWN,0,0);
214
215 //Enter a variable into the global variable map. The loop will exit
216 //when this is set false.
217 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 1);
218
219 // cout <<"*****" <<myEval->gGlobalVariableMap.RetrieveValue("gKeepLooping") << "--"<< mStates.size() << std::endl;
220
221
222 //Loop until gKeepLooping becomes false
223 //In Emscripten, emscripten_sleep() yields to browser between iterations
224 while(mNumStates > 0 &&
225 (pInt)(myEval->gGlobalVariableMap.RetrieveValue("gKeepLooping")))
226 {
227 //Process one iteration of event checking
228 returnval = Loop1();
229
230#ifdef PEBL_ITERATIVE_EVAL
231 //If Loop1() scheduled a callback, execute it now
232 //The callback schedules 4 nodes initially, but lambdaNode pushes the entire function body
233 //Execute until the callback completes (all scheduled nodes consumed)
234 if(mCallbackScheduled)
235 {
236 //Save the node stack size before we scheduled the callback
237 //We want to execute until we're back to this size (all callback nodes consumed)
238 size_t targetNodeStackSize = mCallbackNodeStackSize;
239
240 //Execute nodes until the callback completes
241 //Continue until node stack is back to the size it was before we scheduled the callback
242 while(!myEval->mNodeStack.empty() &&
243 myEval->mNodeStack.size() > targetNodeStackSize)
244 {
245 myEval->Evaluate1(); //Process one node from the stack
246 }
247 }
248#endif
249
250#ifdef PEBL_EMSCRIPTEN
251 // Emscripten: 10ms sleep tied to browser screen refresh
253#else
254 // Unix/Windows: 1ms sleep via platform-specific implementation
256#endif
257 }
258
259 //Clear the event loop when we're done
260 Clear();
261
262 return returnval;
263}
264
265/*
266void LoopAsync(void* data)
267{
268 //cout << "async looping\n";
269 // PEventLoop* eeloop = (PEventLoop*)(data);
270 //PEventLoop * eeloop = Evaluator::
271
272 //cout << "Name: " << Evaluator::mEventLoop->mTmp << endl;
273
274 Evaluator::mEventLoop->Loop1();
275}
276*/
277
278
279//This processes ONE iteration of the event loop.
280//Checks all registered events once, schedules any matching callbacks, then returns.
281//The caller (Loop) will execute the scheduled callbacks.
283{
284 PEvent returnval(PDT_UNKNOWN,0,0);
285 unsigned int result =0;
286 bool matched = false;
287
288 //Reset callback flag
289 mCallbackScheduled = false;
290
291 // Prime the event queue (process pending SDL events)
293
294 //Check event queue events FIRST (keyboard, mouse) so they have priority over timers
295 for(int i = 0; i < mNumStates; i++)
296 {
297 if(!mIsEvent[i]) //Skip non-event states (timers) in this first pass
298 continue;
299
300 //The test is for an event queue-type event.
301 // Note: 'events' contrast with 'states', handled later.
302 // These are devices which send events through the PEBL Event queue.
303 // So, if the current test is an 'event' state, we need to check the event queue.
304
305 //Only test the event if the queue is not empty.
306 if(!gEventQueue->IsEmpty())
307 {
308 //Now, we only should test an event if it is the proper device type.
309
310 if(gEventQueue->GetFirstEventType() == mStates[i]->GetDeviceType())
311 {
312 //Now, just test the device.
313 //I don't think any devices support TestDevice currently.
314 result = mStates[i]->TestDevice();
315
316 if(result)
317 {
318 returnval = gEventQueue->GetFirstEvent();
319
320
321 if(mFunctionNames[i].GetString() != "") //Execute callback if function name exists
322 {
323 //Save node stack size before scheduling callback
324 mCallbackNodeStackSize = myEval->mNodeStack.size();
325
326 //Add the parameters, as a list, to the stack.
327 //Need to add the returnval (event) to the parameter list
328 //just like the recursive evaluator does in PEventLoop.cpp
329
330 Variant parlist = mParameters[i];
331
332 const PList *tmp = parlist.GetComplexData()->GetList();
333
334 PList * list = new PList(*tmp);
335 list->PushBack(Variant(returnval));
336
338 PComplexData * pcd = new PComplexData(list2); // Heap allocation - will be managed by Variant
339
340
341 //Create a DataNode containing the parameter list
342 //This will be evaluated by PEBL_FUNCTION and push the list onto the stack
343 DataNode * paramsNode = new DataNode(Variant(pcd), "", -1);
344
345 //Create a DataNode for the function name
346 DataNode * funcNameNode = new DataNode(mFunctionNames[i], "", -1);
347
348 //Create PEBL_FUNCTION node with parameter DataNode as right child
349 //When PEBL_FUNCTION executes, it will evaluate paramsNode which pushes the parameter list
350 OpNode * functionCallNode = new OpNode(PEBL_FUNCTION, (PNode*)funcNameNode, (PNode*)paramsNode, "event-callback", -1);
351
352 //Mark that we scheduled a callback (for Loop() to execute)
353 mCallbackScheduled = true;
354
355 //Schedule nodes to execute the function and pop its return value
356
357 //Pop the callback's return value (we don't need it)
358 const OpNode * popResult = new OpNode(PEBL_STATEMENTS_TAIL1,NULL,NULL,"event-callback",-1);
359 myEval->NodeStackPush(popResult);
360
361 //Execute the function call (this will handle all stack management)
362 myEval->NodeStackPush(functionCallNode);
363
364 }
365 else //If mNodes[i] is null, terminate
366 {
367
368 //cout << "Stopping the loop. Donk.\n";
369 //We are aborting here. no need to reprogram anything.
370 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 0);
371
372 }
373 matched = true;
374 break;
375 }
376 }
377 }
378
379 //cout << "END Event code\n";
380 }
381
382 //If no event queue match and queue not empty, pop the non-matching event
383 if(!matched && !gEventQueue->IsEmpty())
384 {
386 }
387
388 //If no event queue match, check timer/state events (second pass)
389 if(!matched)
390 {
391 for(int i = 0; i < mNumStates; i++)
392 {
393 if(mIsEvent[i]) //Skip event states in this second pass
394 continue;
395
396 //this is where time events (wait) land.
397 //cout << "STATE type\n";
398 //mStates[i] isn't a device-type state.
399
400 //The test examines the device's state directly.
401
402 result = mStates[i]->TestDevice();
403 if(result)
404
405 {
406 //We need to create a 'dummy' event to use here.
407 PEBL_DummyEvent pde;
408 pde.value = mStates[i]->GetState(mStates[i]->GetInterface());
409
410
411 if(mStates[i]->GetDevice()->GetDeviceType() == PDT_TIMER)
412 {
413
414 returnval = PEvent(PDT_TIMER,0,0);
415 returnval.SetDummyEvent(pde);
416
417 }
418 else if(mStates[i]->GetDevice()->GetDeviceType() == PDT_KEYBOARD)
419 {
420 //This is a keyboard state; different from a keypress event!
421
422 pde.value = (PEBL_Keycode)(mStates[i]->GetInterface());
423
424 returnval = PEvent(PDT_DUMMY,0,0);
425 returnval.SetDummyEvent(pde);
426
427 }
428 else
429 {
430
431 //If this was a time-check event, make a PDT_timer
432 //time needs to go in the 0 below.
433 //returnval = PEvent(PDT_DUMMY,0,0);
434
435 returnval = PEvent((PEBL_DEVICE_TYPE)pde.value,0,0);
436 returnval.SetDummyEvent(pde);
437 }
438 //If mNodes[i] is null, terminate
439
440 if(mFunctionNames[i].GetString() != "") //Execute callback if function name exists
441 {
442 //Save node stack size before scheduling callback
443 mCallbackNodeStackSize = myEval->mNodeStack.size();
444
445 //Add the parameters, as a list, to the stack.
446 //Need to add the returnval (event) to the parameter list
447 //just like the recursive evaluator does in PEventLoop.cpp
448
449 Variant parlist = mParameters[i];
450
451 const PList *tmp = parlist.GetComplexData()->GetList();
452
453 PList * list = new PList(*tmp);
454 list->PushBack(Variant(returnval));
455
457 PComplexData * pcd = new PComplexData(list2); // Heap allocation - will be managed by Variant
458
459 //Create a DataNode containing the parameter list
460 //This will be evaluated by PEBL_FUNCTION and push the list onto the stack
461 DataNode * paramsNode = new DataNode(Variant(pcd), "", -1);
462
463 //Create a DataNode for the function name
464 DataNode * funcNameNode = new DataNode(mFunctionNames[i], "", -1);
465
466 //Create PEBL_FUNCTION node with parameter DataNode as right child
467 //When PEBL_FUNCTION executes, it will evaluate paramsNode which pushes the parameter list
468 OpNode * functionCallNode = new OpNode(PEBL_FUNCTION, (PNode*)funcNameNode, (PNode*)paramsNode, "event-callback", -1);
469
470 //Mark that we scheduled a callback (for Loop() to execute)
471 mCallbackScheduled = true;
472
473 //Schedule nodes to execute the function and pop its return value
474
475 //Pop the callback's return value (we don't need it)
476 const OpNode * popResult = new OpNode(PEBL_STATEMENTS_TAIL1,NULL,NULL,"event-callback",-1);
477 myEval->NodeStackPush(popResult);
478
479 //Execute the function call (this will handle all stack management)
480 myEval->NodeStackPush(functionCallNode);
481
482 }
483 else
484 {
485
486
487 myEval->gGlobalVariableMap.AddVariable("gKeepLooping", 0);
488
489 }
490 matched = true;
491 //Don't break - allow other timer states to fire in same iteration
492 //break;
493
494 } else
495 {
496
497 //cout << "state test is 0; \n";
498 }
499 } //End second for loop (timer/state events)
500 } //End if(!matched)
501
502 // After checking all events, pop matched event queue events
503 if(matched && returnval.GetType() != PDT_TIMER)
504 {
505 //Pop the matched event from queue
507 }
508
509 // Return the event (or UNKNOWN if no match)
510 // The caller (Loop) will call this again to continue checking events
511 return returnval;
512}
513
514
515//Overload of the << operator
516std::ostream & operator <<(std::ostream & out, const PEventLoop & loop )
517{
518
519 out << "PEBL Event Loop:" << flush;
520
521 return out;
522}
#define NULL
Definition BinReloc.cpp:317
#define pInt
Definition Defs.h:8
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
std::ostream & operator<<(std::ostream &out, const PEventLoop &loop)
PlatformEventQueue * gEventQueue
PEBL_Keycode
Definition PKeyboard.h:78
@ P_DATA_FUNCTION
Definition Variant.h:43
void NodeStackPush(const PNode *node)
bool Evaluate1()
static VariableMap gGlobalVariableMap
PList * GetList() const
std::vector< Variant > mParameters
std::string mTmp
std::vector< bool > mIsEvent
std::vector< PNode * > mNodes
PEvent Loop()
Initiates the looping tests.
PEvent Loop1()
Initiates the looping tests.
void RegisterEvent(DeviceState *state, const std::string &function, Variant parameters)
void RegisterState(DeviceState *state, const std::string &function, Variant parameters)
~PEventLoop()
This is the standard pNode destructor.
std::vector< Variant > mFunctionNames
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
PEBL_DEVICE_TYPE GetType() const
Definition PEvent.h:167
void SetDummyEvent(const PEBL_DummyEvent &evt)
Definition PEvent.cpp:183
Definition PList.h:45
void PushBack(const Variant &v)
Definition PList.cpp:149
Definition PNode.h:45
virtual void Sleep(unsigned long int msecs)
void AddVariable(const std::string &varname, const Variant &val)
Variant RetrieveValue(const std::string &varname)
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
PlatformTimer myTimer
pDouble RandomUniform()
pInt Round(pDouble val)