PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PEBL.cpp File Reference
#include "Globals.h"
#include "../base/Evaluator.h"
#include "../devices/PEventLoop.h"
#include "../base/grammar.tab.hpp"
#include "../base/PNode.h"
#include "../base/Loader.h"
#include "../base/PComplexData.h"
#include "../base/PList.h"
#include "../base/FunctionMap.h"
#include "../base/VariableMap.h"
#include "../base/Variant.h"
#include "../libs/PEBLObjects.h"
#include "../utility/PError.h"
#include "../utility/PEBLPath.h"
#include "../utility/PEBLUtility.h"
#include "../utility/rc_ptrs.h"
#include "../utility/BinReloc.h"
#include <iostream>
#include <signal.h>
#include <list>
#include <string>
#include <algorithm>
#include <fstream>
#include "../platforms/sdl/PlatformEnvironment.h"
#include "../platforms/sdl/SDLUtility.h"

Go to the source code of this file.

Functions

void SignalTestComplete (const char *status="completed")
 
PNodeparse (const char *filename)
 
std::list< string > GetFiles (int argc, std::vector< std::string > argv)
 
void PrintOptions ()
 
int PEBLInterpret (int argc, std::vector< std::string > argv)
 
void CaptureSignal (int signal)
 
int main (int argc, char *argv[])
 

Variables

EvaluatormyEval = NULL
 
PlatformEnvironmentmyEnv = NULL
 
LoadermyLoader
 
PNodehead
 

Function Documentation

◆ CaptureSignal()

void CaptureSignal ( int  signal)

Definition at line 930 of file PEBL.cpp.

931{
932 cerr << "Exiting PEBL because of captured signal.\n";
933
934#ifdef WIN32
935 // Restore back when going to windowed or shutting down
936 AllowAccessibilityShortcutKeys( false );
937
938#endif // WIN32
939
940 // Signal completion to JavaScript launcher (for test chains)
941 SignalTestComplete("signal");
942
944
945 if(myLoader) delete myLoader;
946 if(myEnv) delete myEnv;
948
949
950 //Evaluator::gGlobalVariableMap.DumpValues();
951
952#ifdef PEBL_MOVIES
953 //Close the wave player library.
954 WV_waaveClose();
955#endif
956
957
958 //quit SDL here. It should be killed else
959 SDL_Quit();
960
961
962 //Something is not being cleaned up still.
963 raise(signal);
964 exit(1); // Exit with error code 1 when terminated by signal
965}
void SignalTestComplete(const char *status="completed")
Definition PEBL.cpp:61
PlatformEnvironment * myEnv
Definition PEBL.cpp:189
Loader * myLoader
Definition PEBL.cpp:219
static VariableMap gGlobalVariableMap
static FunctionMap mFunctionMap
Initiate some static member data.

References FunctionMap::Destroy(), VariableMap::Destroy(), Evaluator::gGlobalVariableMap, Evaluator::mFunctionMap, myEnv, myLoader, and SignalTestComplete().

Referenced by main().

◆ GetFiles()

std::list< std::string > GetFiles ( int  argc,
std::vector< std::string >  argv 
)

Definition at line 1185 of file PEBL.cpp.

1186{
1187
1188 std::list<std::string> tmp;
1189 std::vector<std::string>::iterator i = argv.begin();
1190
1191
1192// i++;
1193
1194 while(i != argv.end())//int i = 1; i < argc; i++) //skip the first argv, which is just executable name.
1195 {
1196 if(i->compare( "-v")==0 ||
1197 i->compare("-V")==0 ||
1198 i->compare( "-s")==0 ||
1199 i->compare( "-S") == 0 ||
1200 i->compare( "--language")==0 ||
1201 i->compare("--pfile")==0 ||
1202 i->compare("--upload")==0)
1203
1204 {
1205 //This is the variable switch. get rid of it and the next argument.
1206 i++;
1207 }
1208 else if(i->compare("--lsl")==0)
1209 {
1210 // LSL flag - check if next arg is a stream name (not a flag or .pbl file)
1211 std::vector<std::string>::iterator next = i;
1212 next++;
1213 if(next != argv.end() &&
1214 next->c_str()[0] != '-' &&
1215 next->find(".pbl") == std::string::npos)
1216 {
1217 // Next arg is stream name, skip it too
1218 i++;
1219 }
1220 // Otherwise just skip the --lsl flag itself
1221 }
1222 else if (i->compare("--driver")==0 ||
1223 i->compare("--display")==0 ||
1224 i->compare("--depth")==0 )
1225
1226 {
1227 //This is a video driver switch.
1228 i++;
1229 }
1230 else if (i->compare("--windowed")==0 ||
1231 i->compare("--fullscreen")==0 ||
1232 i->compare("--unicode")==0 ||
1233 i->compare("--resizeable")==0 ||
1234 i->compare("--resizable")==0||
1235 i->compare("--vsyncoff")==0||
1236 i->compare("--vsyncon")==0 ||
1237 i->compare("--softrender")==0 ||
1238 i->compare("--help")==0 ||
1239 i->compare("--showtestresults")==0
1240 )
1241
1242
1243 {
1244 //Don't bother incrementing 'i'
1245 }
1246 else
1247 {
1248 //Any other command line arguments are files to load.
1249 // cout << "Adding: [" << *i << "]" << endl;
1250
1251 tmp.push_back(std::string(*i));
1252 }
1253 i++;
1254 }
1255 return tmp;
1256}

Referenced by PEBLInterpret().

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 968 of file PEBL.cpp.

969{
970
971#ifdef PEBL_EMSCRIPTEN
972 EM_ASM(console.log("=== main() called in PEBL.cpp ==="));
973#endif
974
975#ifdef PEBL_WIN32
976// With NO_STDIO_REDIRECT defined, SDL doesn't create stdout.txt/stderr.txt
977// Don't use freopen - inherit stdout/stderr from parent process
978// This allows shell redirection (> stdout.txt 2> stderr.txt) to work
979// Note: stdout/stderr may not exist if run from GUI, but will work from terminal
980#endif // PEBL_WIN32
981
982
983
984 int newargc = argc;
985 // Set up some signals to capture
986#ifdef SIGHUP
987 signal(SIGHUP, CaptureSignal);
988#endif
989
990#ifdef SIGKILL
991 signal(SIGKILL, CaptureSignal);
992#endif
993
994#ifdef SIGSTOP
995 signal(SIGSTOP, CaptureSignal);
996#endif
997
998 //#ifdef SIGTERM
999 // signal(SIGTERM, CaptureSignal);
1000 //#endif
1001
1002 signal(SIGINT, CaptureSignal);
1003
1004
1005#ifdef SIGQUIT
1006 signal(SIGQUIT, CaptureSignal);
1007#endif
1008
1009
1010 signal(SIGTERM, CaptureSignal);
1011
1012 //char** new_argv = NULL;
1013 std::vector<std::string> newargv;
1014
1015
1016//Put code here that runs on only one of the platforms
1017
1018#if defined(PEBL_OSX)
1019
1020 //Find the location of the app bundle; save it to global variables etc.:
1021 CFBundleRef mainBundle = CFBundleGetMainBundle();
1022 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
1023 char resourcepath[PATH_MAX];
1024 if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)resourcepath, PATH_MAX))
1025 {
1026 PError::SignalFatalError("Unable to identify resource location.\n");// error!
1027 }
1028 CFRelease(resourcesURL);
1029
1030
1031 //Identify location for the launcher script, which should be in the resources path.
1032 std::string script = "/launcher.pbl";
1033 std::string basedir = (std::string)resourcepath ;
1034 std::string launch = basedir + script;
1035
1036 Evaluator::gGlobalVariableMap.AddVariable("gPEBLResourcePath",Variant(resourcepath));
1037 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBasePath",Variant(basedir));
1038 std::cerr << "Basedir:" << basedir << endl;
1039 std::cerr << "launch: " << launch << endl;
1040
1041
1042#elif defined(PEBL_WIN32)
1043
1044
1045 SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &g_StartupStickyKeys, 0);
1046 SystemParametersInfo(SPI_GETTOGGLEKEYS, sizeof(TOGGLEKEYS), &g_StartupToggleKeys, 0);
1047 SystemParametersInfo(SPI_GETFILTERKEYS, sizeof(FILTERKEYS), &g_StartupFilterKeys, 0);
1048
1049 // Disable when usuing PEBL (reenable later?)
1050 AllowAccessibilityShortcutKeys( false );
1051 string basedir = PEBLUtility::StripFile(argv[0]) + "..\\";
1052
1053
1054 std::string resourcepath = basedir;
1055 std::string launch = "launcher.pbl";
1056 std::string script = resourcepath+"/pebl-lib/"+launch;
1057
1058
1059 Evaluator::gGlobalVariableMap.AddVariable("gPEBLResourcePath",Variant(basedir));
1060 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBasePath",Variant(basedir));
1061
1062
1063#elif defined (PEBL_LINUX)
1064
1065
1066 string basedir;
1067 BrInitError error;
1068 if (br_init (&error) == 0 && error != BR_INIT_ERROR_DISABLED)
1069 {
1070 PError::SignalWarning("Warning: BinReloc failed to initialize.\n Will fallback to current directory.\n");
1071 basedir = "./";
1072 } else {
1073 // Get directory containing the executable
1074 char* exe_dir = br_find_exe_dir("");
1075 if (exe_dir != NULL) {
1076 // Go up one level from bin/ to get base directory
1077 basedir = string(exe_dir) + string("/../");
1078 std::cerr << "Executable directory: [" << exe_dir << "]\n";
1079 free(exe_dir);
1080 } else {
1081 basedir = "./";
1082 }
1083 }
1084
1085 std::string resourcepath = basedir;
1086 std::string launch = basedir + "bin/launcher.pbl";
1087 std::string script = "";
1088
1089// cout << "resources: " << resourcepath << endl;
1090// cout << "script: " << script<< endl;
1091// cout << "launch: " << launch << endl;
1092 Evaluator::gGlobalVariableMap.AddVariable("gPEBLResourcePath",Variant(resourcepath));
1093 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBasePath",Variant(basedir));
1094
1095
1096#elif defined (PEBL_EMSCRIPTEN)
1097
1098 std::string basedir = "/usr/local/share/pebl2";
1099
1100 std::string resourcepath = basedir;
1101 // For Emscripten, script path should always be passed via Module.callMain()
1102 // No default launcher - require explicit arguments
1103 std::string launch = "";
1104 std::string script = "";
1105
1106 Evaluator::gGlobalVariableMap.AddVariable("gPEBLResourcePath",Variant(resourcepath));
1107 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBasePath",Variant(basedir));
1108
1109#endif
1110
1111
1112 //Identify home directory; it is an environment variable.
1113 std::string home = "";
1114 char* val = getenv("HOME");
1115 if(val)
1116 {
1117 home = val;
1118 }
1119
1120
1121 //If there are no command-line arguments (including files),
1122 //print usage and exit. The native pebl-launcher handles the GUI on Linux.
1123
1124 if(newargc == 1)
1125 {
1126#if defined(PEBL_LINUX)
1127 // On Linux, pebl2 requires an explicit script argument.
1128 // The GUI launcher (pebl-launcher) handles the no-argument case.
1129 PrintOptions();
1130 cout << "\nTo launch the graphical study manager, run: pebl-launcher\n";
1131 return 0;
1132#elif defined(PEBL_WIN32)
1133 // On Windows, a double-click on pebl2.exe should open the GUI launcher.
1134 // pebl-launcher.exe lives in the same bin\ directory as pebl2.exe.
1135 {
1136 std::string launcherExe = PEBLUtility::StripFile(argv[0]) + "pebl-launcher.exe";
1137 std::cerr << "No script specified. Launching: " << launcherExe << "\n";
1138 HINSTANCE result = ShellExecuteA(NULL, "open",
1139 launcherExe.c_str(),
1140 NULL,
1141 PEBLUtility::StripFile(argv[0]).c_str(),
1142 SW_SHOWNORMAL);
1143 if ((INT_PTR)result <= 32)
1144 {
1145 // ShellExecute failed (returns value <= 32 on error).
1146 // Fall back to printing help so the user knows what to do.
1147 PrintOptions();
1148 cerr << "\nNote: pebl-launcher.exe not found at " << launcherExe << "\n";
1149 }
1150 return 0;
1151 }
1152#else
1153 // macOS / other: just print help.
1154 PrintOptions();
1155 return 0;
1156#endif
1157
1158 } else{
1159 //This is what happens when argc != 1 (when there ARE arguments), on any platform
1160 newargc=0;
1161 for(int i=0; i < argc; i++)
1162 {
1163 newargv.push_back(argv[i]);
1164 newargc++;
1165 //cout << argv[i] << endl;
1166 }
1167 }
1168
1169
1170
1171 Evaluator::gGlobalVariableMap.AddVariable("gWorkingDirectory",
1173
1174
1175 std::cerr<< "Working directory: " << PEBLUtility::GetWorkingDirectory() << endl;
1176
1177
1178
1179
1180 return PEBLInterpret(newargc, newargv);
1181}
#define NULL
Definition BinReloc.cpp:317
int br_init(BrInitError *error)
Definition BinReloc.cpp:338
char * br_find_exe_dir(const char *default_dir)
Definition BinReloc.cpp:405
BrInitError
Definition BinReloc.h:22
@ BR_INIT_ERROR_DISABLED
Definition BinReloc.h:32
void PrintOptions()
Definition PEBL.cpp:1258
int PEBLInterpret(int argc, std::vector< std::string > argv)
Definition PEBL.cpp:229
void CaptureSignal(int signal)
Definition PEBL.cpp:930
void AddVariable(const std::string &varname, const Variant &val)
const std::string StripFile(const std::string &file)
Variant GetWorkingDirectory()
void SignalWarning(const std::string &message)
Definition PError.cpp:119
void SignalFatalError(const std::string &message)

References VariableMap::AddVariable(), br_find_exe_dir(), br_init(), BR_INIT_ERROR_DISABLED, CaptureSignal(), PEBLUtility::GetWorkingDirectory(), Evaluator::gGlobalVariableMap, NULL, PEBLInterpret(), PrintOptions(), PError::SignalFatalError(), PError::SignalWarning(), and PEBLUtility::StripFile().

◆ parse()

PNode * parse ( const char *  filename)

This is the main PEBL interpreter program. It takes files as command-line arguments, which it parses with the bison parser, creating a single tree of PNodes. Then, it feeds this tree into the loader, which loads the individual functions into a function map. Then, it sets up any global entities: a graphical environment, a timer, a global variable map. Finally, it locates the 'Start' function and executes it.

◆ PEBLInterpret()

int PEBLInterpret ( int  argc,
std::vector< std::string >  argv 
)

Definition at line 229 of file PEBL.cpp.

230{
231
232#ifdef PEBL_EMSCRIPTEN
233 EM_ASM(console.log("=== PEBLInterpret() called ==="));
234#endif
235
236 std::cerr << "**************Starting PEBLInterpret\n";
237 std::cerr << "**************argc:" << argc << endl;
238
239#if defined(PEBL_UNIX) and not defined(PEBL_OSX)
240 if(argc==2 && strcmp(argv[1].c_str(), "--install")==0)
241 {
242 string basedir;
243 BrInitError error;
244 if (br_init (&error) == 0 && error != BR_INIT_ERROR_DISABLED)
245 {
246 PError::SignalWarning("Warning: BinReloc failed to initialize.\n Will fallback to hardcoded default path.\n");
247 //basedir = "/usr/local/share/pebl/";
248 basedir = "/usr/local/share/pebl2";
249 }
250
251 string prefix = br_find_prefix("/usr/local/");
252 basedir = prefix + string("/share/pebl2/battery/");
253 string destdir = string("~/Documents/pebl-exp.") + PEBL_VERSION;
254
255 //Now, copy everything in 'battery' to your documents directory.
256 //std::cerr << "Creating Documents/pebl-exp.0.14 Directory\n";
257 PEBLUtility::SystemCall("mkdir ~/Documents","");
258 PEBLUtility::SystemCall("mkdir "+destdir,"");
259 std::cerr << "Copying files to ["+destdir+ "]\n";
260 PEBLUtility::SystemCall("cp -R "+ basedir + " " + destdir,"");
261 exit(0);
262 }
263#endif
264
265 PNode * tmp = NULL;
266
267 //Cycle through command-line parameters extracting the files.
268 //This does not check for file validity, it just removes any other command-line options,
269 //i.e. ones that are of the form <-flag> <option> or whatever.
270 //
271
272#if 1
273 //THis is just for debugging purposes.
274 cerr << "************Arguments: "<< argv.size()<<"\n";
275 std::vector<std::string>::iterator ii = argv.begin();
276 while(ii != argv.end())
277 {
278 cerr << *ii << endl;
279 ii++;
280 cerr << "********\n";
281 }
282#endif
283
284
285 //get the test file from the command line:
286 std::list<std::string> files = GetFiles(argc, argv);
287
288 //Set up the search path.
290
291 cerr << "PATH:" << Evaluator::gPath;
292
293 //Add the built-in PEBL libraries to the files list.
294 files.push_back("Design.pbl");
295 files.push_back("Utility.pbl");
296 files.push_back("Math.pbl");
297 files.push_back("Graphics.pbl");
298 files.push_back("UI.pbl");
299 files.push_back("HTML.pbl");
300 files.push_back("Transfer.pbl"); // Network/HTTP file transfer functions
301 files.push_back("Layout.pbl"); // Layout & response system with nested properties
302
303#ifdef PEBL_EMSCRIPTEN
304 files.push_back("EM.pbl");
305
306#endif
307 // files.push_back("Taguchi.pbl"); //not ready
308
309 //this was already loaded on the command-line.
310 //load/run test.pbl here.
311#ifdef PEBL_EMSCRIPTEN
312 //std::cout << "Loading filename:[test.pbl]\n";
313 //files.push_back("test.pbl");
314#endif
315
316
317 //Process the first command-line argument.
318 std::list<std::string>::iterator i = files.begin();
319 i++;
320
321 //-----------------------------------------------------------
322 // Process all files on the command-line
323 //-----------------------------------------------------------
324
325 std::cerr << "Loading filename:[" << *i << "]\n";
326 string inputfilename = Evaluator::gPath.FindFile(*i);
327 string otherfilename;
328
329 head = NULL;
330 if(inputfilename != "")
331 {
332 cerr << "Processing PEBL Source File1: " << inputfilename << endl;
333 head = parse(inputfilename.c_str());
334 }
335 else
336 {
337 PError::SignalFatalError("Unable to find file: [" + inputfilename + "].");
338 }
339 i++;
340 //If there are any more arguments, process them by accomodating them
341 //inside a function list.
342
343 //Increment the iterator to move to the second command-line
344 // i++;
345 while(i != files.end())
346 {
347 std::cerr << "********************\n";
348 std::cerr << "Loading file name: [" << *i <<"]"<< endl;
349 otherfilename = Evaluator::gPath.FindFile(*i);
350 std::cerr << "Resolved as: [" <<otherfilename <<"]"<< endl;
351 if(inputfilename != "")
352 {
353 cerr << "Processing PEBL Source File2: " << otherfilename << endl;
354
355 //A filename could be a directory (e.g., with media in it.)
356 //If so, don't parse it.
357 if(!Evaluator::gPath.IsDirectory(otherfilename))
358 {
359 //Make a new node.
360 tmp = parse(otherfilename.c_str());
361
362 //Now, make a new node that contains head and tmp.
363 head = new OpNode(PEBL_FUNCTIONS, head, tmp, "INTERNAL PEBL STRUCTURE", -1);
364 }
365 }
366 else
367 {
368 PError::SignalFatalError("Unable to find file: ["+*i+"] at [" + otherfilename + "]");
369 //ignore mis-loaded files after the first; this is causing us
370 //problems on osx
371 }
372 i++;
373 }
374
375 // Done processing files.
376 //-----------------------------------------------------
377
378 cerr << "---------Loading Program---------" << endl;
379 //Now, load it into the environment:
380
381 // Create a loader that will load functions into the functionmap
382 myLoader = new Loader();
384
385
386 cerr <<"Analyzing code for functions." << endl;
388
389
390 cerr << "Loading Library functions." << endl;
392
393 //This just destroys the function tree, not the
394 //parsed node structure that is contained within
395 //mFunctionMap.
396 //cerr << "Removing residual function tree\n";
397 ((OpNode*)head)->DestroyFunctionTree();
398 delete head;
399 head = NULL;
400
401#if 0
402 cerr << "\n\n--------------------------------\n";
403 cerr << "Functions used in program: " << endl;
404 cerr << "--------------------------------\n";
406 cerr << "--------------------------------\n\n";
407#endif
408
409 //Parse command-line arguments.
410
411 PList * pList = new PList();
412 PList * arglist = new PList();
413
414
415 //Use the current screen resolution as a startingp
416
417
418 //Initialize display size here with a non-interesting one.
419 //It may get set by a command-line argument later.
420 std::string displaySize="0x0";
421
422
423 std::string depth = "32"; //used to be 16; does this matter?
424 enum PEBLVideoMode displayMode;
425 enum PEBLVideoDepth displayDepth;
426 bool windowed = true;
427 bool vsync = false; //default vsync to false. This is reliant on hardware that might not work well on all systems.
428 bool softrender = false;
429 bool resizeable = false;
430 bool unicode = true;
431 bool upload = false;
432 bool showHelp = false;
433 bool showTestResults = false;
434 bool lslEnabled = false;
435
436 Variant uploadConfigFile = "";
437 Variant lslStreamName = ""; // Optional custom stream name
438
439 Variant lang = "";
440 Variant subnum = 0;
441
442
443 std::string parpath = PEBLUtility::StripFile(inputfilename);
444 std::string pname = PEBLUtility::GetBaseFileName(inputfilename);
445
446 //default the parameter file to ./params/SCRIPTNAME.par.json, but normally this will get overwritten below
447 Variant pfile = Variant(parpath) + Variant("params/") + Variant(pname)+Variant(".par.json");
448
449 //Extract the command-line variables to bind
450 for(int j = 1; j < argc; j++)
451 {
452
453 if(strcmp(argv[j].c_str(), "-v")==0 ||
454 strcmp(argv[j].c_str(), "-V")==0)
455 {
456 Variant tmp = argv[++j];
457 //cout <<"Content of passed-in variable " << j << ":" << tmp << endl;
458 arglist->PushBack(tmp);
459 }
460
461 else if(strcmp(argv[j].c_str(), "-s")==0 ||
462 strcmp(argv[j].c_str(), "-S")==0)
463 {
464 subnum = argv[++j];
465 //cout << "collecting Subject Number "<< subnum << endl;
466 }
467 //set the driver directly from the command-line, if necessary.
468 else if(strcmp(argv[j].c_str(),"--driver")==0)
469 {
470 if(j+1 < argc)
471 {
472 j++;
473 Evaluator::gGlobalVariableMap.AddVariable("gDriverHint",argv[j].c_str());
474 }
475 }
476 else if(strcmp(argv[j].c_str(),"--display")==0)
477 {
478 displaySize = argv[++j];
479 }
480
481 else if(strcmp(argv[j].c_str(),"--depth")==0)
482 {
483 depth = argv[++j];
484
485 }
486
487 else if (strcmp(argv[j].c_str(),"--fullscreen")==0)
488 {
489 windowed = false;
490 }
491 else if(strcmp(argv[j].c_str(),"--windowed")==0)
492 {
493 windowed = true;
494 }
495 else if(strcmp(argv[j].c_str(),"--unicode")==0)
496 {
497 unicode = true;
498 }
499 else if(strcmp(argv[j].c_str(),"--language")==0)
500 {
501 lang = argv[++j];
502 }
503
504 else if(strcmp(argv[j].c_str(),"--pfile")==0)
505 {
506 std::string pfileArg = argv[++j];
507 // Check if it's a URL - if so, use it directly without prepending "params/"
508 if(pfileArg.compare(0, 7, "http://") == 0 || pfileArg.compare(0, 8, "https://") == 0)
509 {
510 pfile = Variant(pfileArg);
511 }
512 else
513 {
514 pfile = Variant("params/") + Variant(pfileArg);
515 }
516 }
517
518 else if(strcmp(argv[j].c_str(),"--upload")==0)
519 {
520
521 upload = true;
522 uploadConfigFile = Variant(argv[++j]); //Pass the upload config file in
523 cerr << "setting upload file: [" << uploadConfigFile << "]\n";
524 }
525
526 else if(strcmp(argv[j].c_str(),"--lsl")==0)
527 {
528 lslEnabled = true;
529 // Check if next argument is a stream name (not another flag)
530 if(j+1 < argc && argv[j+1].c_str()[0] != '-')
531 {
532 lslStreamName = Variant(argv[++j]);
533 cerr << "LSL enabled with stream name: [" << lslStreamName << "]\n";
534 }
535 else
536 {
537 cerr << "LSL enabled (auto-generate stream name)\n";
538 }
539 }
540
541 else if(strcmp(argv[j].c_str(),"--resizeable")==0 ||
542 strcmp(argv[j].c_str(),"--resizable")==0 )
543 {
544 if(windowed)
545 resizeable = true;
546 }
547 else if(strcmp(argv[j].c_str(),"--vsyncon")==0)
548 {
549 vsync = true;
550 }
551
552 else if(strcmp(argv[j].c_str(),"--vsyncoff")==0)
553 {
554 vsync = false;
555 }
556 else if(strcmp(argv[j].c_str(),"--softrender")==0)
557 {
558 softrender = true;
559 }
560 else if(strcmp(argv[j].c_str(),"--help")==0)
561 {
562 showHelp = true;
563 }
564 else if(strcmp(argv[j].c_str(),"--showtestresults")==0)
565 {
566 showTestResults = true;
567 }
568
569
570
571 }
572
573
574 //Now, set the display modes variables based on the command-line options.
575 displayMode = PEBLUtility::GetVideoMode(displaySize);
576 displayDepth = PEBLUtility::GetVideoDepth(depth);
577
578
579
580 //This sets the video driver, and other platform-specific stuff.
581#if defined(PEBL_UNIX)
582
583
584
585 //Now, set the priority to the highest it can go.
586
587 cerr << "Priority set here**************************\n";
588 int priority = getpriority(PRIO_PROCESS,0);
589 cerr << "Process running at a nice value of " << priority << endl;
590 cerr << "attempting to set priority to: " << PRIO_MIN << endl;
591 //setpriority(PRIO_PROCESS,0,0);
592 setpriority(PRIO_PROCESS,0,PRIO_MIN);
593 priority = getpriority(PRIO_PROCESS,0);
594
595 cerr << "Process running at a nice value of " << priority << endl;
596
597 /*
598 struct sched_param mysched;
599 mysched.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
600 if( sched_setscheduler( 0, SCHED_RR, &mysched ) == -1 )
601 {
602 cout << "Unable to enable round-robin scheduling. Must have root priviledges.\n";
603 }
604 else
605 {
606 cout << "Round-robin scheduling enabled.\n";
607 }
608
609 struct timespec interval;
610 if(sched_rr_get_interval(0,&interval)== -1)
611 {
612 cout << "Unable to get Round-robin scheduling interval.\n";
613 }
614 else
615 {
616 cout << "Round Robin Scheduling Interval: [" <<interval.tv_sec * 1000 + interval.tv_nsec / 1000 <<"] ms.\n";
617 }
618
619 */
620
621
622#elif defined(PEBL_WIN32)
623 //Do specific win32 stuff here.
624
625 //SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
626 //REALTIME causes program to hang on keyboard input.
627 SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
628
629 //setenv()
630#endif
631
632 //cout <<"About to create environment\n";
633
634 // We can't use any SDL-related functions before this function is called.
635 // But we may want to know current screen resolution before we set displaymode
636
637 PEBLObjects::MakeEnvironment(displayMode, displayDepth, windowed,resizeable,unicode);
638
639 cerr << "Environment created\n";
640
641
642 //Seed the random number generator with time of day.
643 srand((unsigned int)time(0));
644
645 cerr << "---------Creating Evaluator-----" << endl;
646 //Create evaluator, because it contains a function map as a static member variable.
647 //Create it with the command-line -v parameters as a list bound to the argument.
648
649#if defined(PEBL_EMSCRIPTEN)
650 std::cerr <<"--------o-o-o-o-o-o--\n";
651 arglist->PushBack(Variant(0));
653 pList->PushBack(Variant(pcd));
654#else
655
656
657 //Now, arglist should contain any values specified with the -v flag.
658 if(arglist->Length()==0)
659 {
660 cerr <<"No command line arguments given\n";
661 arglist->PushBack(Variant(0));
663 pList->PushBack(Variant(pcd));
664 }
665 else
666 {
668 pList->PushBack(Variant(pcd));
669 }
670#endif
671
673 Variant v = Variant(pcd2);
674
675 if(showHelp)
676 {
677 PrintOptions();
678 }
679
680
681
682 std::list<PNode> tmpcallstack;
683
684 //myEval is now a global, because we have moved to a single-evaluator model:
685 myEval = new Evaluator(v,"Start");
686
687 //Set the executable name here:
688#if defined(PEBL_LINUX) || defined(PEBL_UNIX)
689 // Check for AppImage first (special case - need .AppImage path, not extracted binary)
690 const char* appimage_path = getenv("APPIMAGE");
691 if (appimage_path != NULL) {
692 // Running from AppImage - use APPIMAGE env var
693 myEval->gGlobalVariableMap.AddVariable("gExecutableName", appimage_path);
694 cerr << "Running from AppImage: " << appimage_path << endl;
695 } else {
696 // Standard Linux installation or local build - use BinReloc
697 char* exe_path = br_find_exe(argv[0].c_str());
698 if (exe_path != NULL) {
699 // br_find_exe succeeded - use absolute path
700 myEval->gGlobalVariableMap.AddVariable("gExecutableName", exe_path);
701 cerr << "Executable path: " << exe_path << endl;
702 free(exe_path); // br_find_exe allocates memory
703 } else {
704 // Fallback: use argv[0] as-is (shouldn't happen on Linux)
705 myEval->gGlobalVariableMap.AddVariable("gExecutableName", argv[0]);
706 PError::SignalWarning("Warning: br_find_exe failed, using argv[0]");
707 }
708 }
709#else
710 // Windows, macOS, Emscripten - use argv[0]
711 myEval->gGlobalVariableMap.AddVariable("gExecutableName", argv[0]);
712#endif
713 myEval->gGlobalVariableMap.AddVariable("gScriptName", Variant(inputfilename));
714 //Set the default screen resolution based on the current one.
716
717 // Extract width and height from SDL display mode for both native and Emscripten builds
718 PList * plist = cursize.GetComplexData()->GetList();
719 Variant width = plist->First();
720 Variant height = plist->Nth(2);
721
722 delete plist;
723 cursize = 0;
724
725 myEval->gGlobalVariableMap.AddVariable("gVideoWidth", width);
726 myEval->gGlobalVariableMap.AddVariable("gVideoHeight", height);
727
728
729 myEval->gGlobalVariableMap.AddVariable("gShowTestResults",Variant(showTestResults));
730 //displaysize may have been set at the command line. If so, we will need to
731 //override it. It is currently a string called displaysize.
732
733
734 size_t found = displaySize.find("x");
735 if(found == string::npos)
736 {
737 //Nothing is found. Use 0s to indicate an invalid displaysize
738 width = 0;
739 height = 0;
740 } else
741 {
742 cerr <<"Size from command line argument: " << displaySize.substr(0,found)<< "|"<< displaySize.substr(found+1) <<endl;
743 //something was found.
744 width = atoi(displaySize.substr(0,found).c_str());
745 height = atoi(displaySize.substr(found+1).c_str());
746 }
747
748
749 if((pInt)width>0 & (pInt)height>0)
750 {
751 Evaluator::gGlobalVariableMap.AddVariable("gVideoWidth",width);
752 Evaluator::gGlobalVariableMap.AddVariable("gVideoHeight",height);
753 }
754
755
756 //This lets you change vsync within the script.
758
759
760 Evaluator::gGlobalVariableMap.AddVariable("gSoftRender",Variant(softrender));
761
762 //Add the subject identifier.
764 //If this is set to 1, we have reset the subject code, and
765 //it is presumably good for all future resets.
767
768 //whether to automatically attempt upload.
769 //if 0 it doesn't upload.
770 //if non-0, that specifies the upload config file
772 Evaluator::gGlobalVariableMap.AddVariable("gUploadFile",uploadConfigFile); //"" if upload is false
773
774 //LSL (Lab Streaming Layer) configuration
775 //Set by --lsl command-line flag
776 //If enabled but no custom name, PEBL wrapper will auto-generate: "PEBL_" + gScriptName
777 Evaluator::gGlobalVariableMap.AddVariable("gLSLEnabled",Variant(lslEnabled));
778 Evaluator::gGlobalVariableMap.AddVariable("gLSLStreamName",lslStreamName); // "" means auto-generate
779
780 //Translate lang to the uppercase 2-letter code
781 std::string tmps =lang;
782 transform(tmps.begin(),tmps.end(),tmps.begin(),toupper);
784
785
786 //this global can't be set in-script; changing it will have no impact.
787 Evaluator::gGlobalVariableMap.AddVariable("gFullscreen",Variant(1-windowed));
788
789 //Add a special 'quote' character.
791
792
793 // Set default base fonts based on language using centralized font selection logic
794 // This ensures consistency with GetFontForLanguageOrScript() utility function
795 std::string baseFont = PEBLUtility::GetFontForLanguageOrScript(tmps, 0); // 0 = sans-serif
796 std::string monoFont = PEBLUtility::GetFontForLanguageOrScript(tmps, 1); // 1 = monospace
797 std::string serifFont = PEBLUtility::GetFontForLanguageOrScript(tmps, 2); // 2 = serif
798
799 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBaseFont", Variant(baseFont));
800 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBaseFontMono", Variant(monoFont));
801 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBaseFontSerif", Variant(serifFont));
802
803 // Always set fallback fonts to DejaVu (optimal for Western/Latin scripts)
804 // These are used when translations aren't available, avoiding issues like:
805 // - RTL languages (AR/HE) right-justifying English text
806 // - CJK languages rendering Latin text in CJK fonts
807 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBaseFontFallback",Variant("DejaVuSans.ttf"));
808 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBaseFontMonoFallback",Variant("DejaVuSansMono.ttf"));
809 Evaluator::gGlobalVariableMap.AddVariable("gPEBLBaseFontSerifFallback",Variant("DejaVuSerif.ttf"));
810
811 //load the parameter file into a global variable
813
814
815 //Do easy-sleep by default:
817 //Now, everything should be F-I-N-E fine.
819
820 if(head)
821 {
822 cerr << "---------Evaluating Program-----" << endl;
823 //Execute everything
824
825
826#ifdef PEBL_ITERATIVE_EVAL
827 // Iterative evaluator - start at head node and run until stack is empty
828 cerr << "Starting evaluation with iterative evaluator\n";
829
830 // Wrap Start() call in proper PEBL_FUNCTION node to ensure call stack is managed correctly
831 // This matches how all other lambda function calls work (PEBL_FUNCTION -> PEBL_FUNCTION_TAIL1 -> PEBL_FUNCTION_TAIL2 -> PEBL_LAMBDAFUNCTION)
832 // Without this wrapper, the call stack push in PEBL_FUNCTION_TAIL2 (line 1103) is skipped,
833 // causing gCallStack to be empty when PEBL_FUNCTION_TAIL_LIBFUNCTION tries to pop (line 1134)
834
835 // Create argument for Start() - pass the command-line arguments list (same as recursive evaluator)
836 // If no args, this will be a list containing 0
837 DataNode* argNode = new DataNode(v, "", 0); // v is the Variant created above (pcd2 containing pList)
838 OpNode* argList = new OpNode(PEBL_LISTITEM, argNode, NULL, "", 0);
839 OpNode* args = new OpNode(PEBL_ARGLIST, argList, NULL, "", 0);
840
841 DataNode* funcNameNode = new DataNode(Variant("START", P_DATA_FUNCTION), "", 0);
842 OpNode* startCall = new OpNode(PEBL_FUNCTION, funcNameNode, args, "", 0);
843
844 myEval->Evaluate1(startCall);
845
846 cerr << "Running evaluator loop\n";
847 while(myEval->GetNodeStackDepth() > 0)
848 {
849 myEval->Evaluate1();
850 }
851#else
852 // Recursive evaluator - traditional single-call evaluation
853 cerr << "Starting evaluation with recursive evaluator\n";
855#endif
856
857 // Capture return value from Start() before cleanup
858 // The return value should be on the evaluator's stack
859 int scriptReturnCode = 0;
860 if (myEval->GetStackDepth() > 0) {
861 Variant returnVal = myEval->Pop();
862 // If the return value is a number, use it as the exit code
863 if (returnVal.IsNumber()) {
864 scriptReturnCode = (int)returnVal;
865 cerr << "Script returned: " << scriptReturnCode << endl;
866 }
867 }
868
869#ifdef PEBL_EMSCRIPTEN
870 // Emscripten: Early return without cleanup (browser manages lifecycle)
871 cerr << "========================================" << endl;
872 cerr << "PEBL program completed successfully." << endl;
873 cerr << "========================================" << endl;
874
875 // Signal completion to JavaScript launcher (for test chains)
876 SignalTestComplete("completed");
877
878 return 0;
879#else
880 // Native platforms: Perform full cleanup
881 // IMPORTANT: Delete myEval FIRST to clean up local variables (child widgets)
882 // before destroying global variables (parent window gWin).
883 // This prevents child widgets from trying to access already-deleted parents.
884 delete ::myEval;
885 ::myEval = NULL;
886
888
889 if(myLoader) delete myLoader;
890 if(myEnv) delete myEnv;
892 //Evaluator::gGlobalVariableMap.DumpValues();
893
894#ifdef PEBL_MOVIES
895 //Close the wave player library.
896 WV_waaveClose();
897#endif
898
899 //Be sure SDL quits. We need to be sure everything is tidied up,
900 //or SDL_Quit will segfault.
901
902 SDL_Quit();
903
904 // Use _exit() to terminate immediately with the script's return code
905 // This avoids running any remaining destructors which can crash due to cleanup order issues
906 cerr << "Exiting with code: " << scriptReturnCode << endl;
907 _exit(scriptReturnCode);
908#endif
909
910
911 //Let's clean up any remaining counted pointers here.
912
913 v = 0;
914
915 return scriptReturnCode;
916 }
917 else
918 {
919 cerr << "Error: Can't evaluate program" << endl;
920
921 if(myLoader) delete myLoader;
922 return 1;
923
924 }
925
926 return 0;
927
928}
char * br_find_prefix(const char *default_prefix)
Definition BinReloc.cpp:433
char * br_find_exe(const char *default_exe)
Definition BinReloc.cpp:377
#define pInt
Definition Defs.h:8
PEBLVideoDepth
Definition Globals.h:69
PEBLVideoMode
Definition Globals.h:58
#define PEBL_VERSION
PNode * head
Definition PEBL.cpp:220
std::list< string > GetFiles(int argc, std::vector< std::string > argv)
Definition PEBL.cpp:1185
Evaluator * myEval
Definition PEBL.cpp:188
PNode * parse()
@ P_DATA_FUNCTION
Definition Variant.h:43
This class has got everything you need to evaluate stuff.
bool Evaluate(const PNode *node)
static PEBLPath gPath
bool Evaluate1()
int GetNodeStackDepth()
Variant Pop()
int GetStackDepth()
void DumpValues()
void LoadUserFunctions(OpNode *node)
Definition Loader.cpp:147
void LoadLibraryFunctions()
Definition Loader.cpp:195
void FindFunctions(const PNode *Node)
Definition Loader.cpp:88
PNode * GetMainPEBLFunction()
Definition Loader.cpp:372
PList * GetList() const
std::string FindFile(const string &filename)
Definition PEBLPath.cpp:368
void Initialize(std::list< std::string >)
Definition PEBLPath.cpp:60
Definition PList.h:45
Variant Nth(unsigned int n)
Definition PList.cpp:181
unsigned long Length() const
Definition PList.h:89
void PushBack(const Variant &v)
Definition PList.cpp:149
Variant First()
Definition PList.cpp:169
Definition PNode.h:45
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
bool IsNumber() const
This tests whether the Variant is a number (i.e., a float or an integer.)
Definition Variant.cpp:930
Variant IsDirectory(Variant v)
void MakeEnvironment(PEBLVideoMode mode, PEBLVideoDepth depth, bool windowed, bool resizeable, bool unicode)
This function instantiates the namespace-viewable.
Variant SystemCall(std::string path, std::string args)
PEBLVideoDepth GetVideoDepth(std::string depthline)
const std::string GetBaseFileName(const std::string &file)
std::string GetFontForLanguageOrScript(const std::string &code, int fontType)
PEBLVideoMode GetVideoMode(std::string modeline)
Variant GetCurrentScreenResolution()

References VariableMap::AddVariable(), br_find_exe(), br_find_prefix(), br_init(), BR_INIT_ERROR_DISABLED, FunctionMap::Destroy(), VariableMap::Destroy(), FunctionMap::DumpValues(), Evaluator::Evaluate(), Evaluator::Evaluate1(), PEBLPath::FindFile(), Loader::FindFunctions(), PList::First(), PEBLUtility::GetBaseFileName(), Variant::GetComplexData(), SDLUtility::GetCurrentScreenResolution(), GetFiles(), PEBLUtility::GetFontForLanguageOrScript(), PComplexData::GetList(), Loader::GetMainPEBLFunction(), Evaluator::GetNodeStackDepth(), Evaluator::GetStackDepth(), PEBLUtility::GetVideoDepth(), PEBLUtility::GetVideoMode(), Evaluator::gGlobalVariableMap, Evaluator::gPath, head, PEBLPath::Initialize(), Variant::IsNumber(), PList::Length(), Loader::LoadLibraryFunctions(), Loader::LoadUserFunctions(), PEBLObjects::MakeEnvironment(), Evaluator::mFunctionMap, myEnv, myEval, myLoader, PList::Nth(), NULL, P_DATA_FUNCTION, parse(), PEBL_VERSION, pInt, Evaluator::Pop(), PrintOptions(), PList::PushBack(), PError::SignalFatalError(), SignalTestComplete(), PError::SignalWarning(), PEBLUtility::StripFile(), and PEBLUtility::SystemCall().

Referenced by main().

◆ PrintOptions()

void PrintOptions ( )

Definition at line 1258 of file PEBL.cpp.

1259{
1260 cout << "-------------------------------------------------------------------------------\n";
1261 cout << "PEBL: The Psychology Experiment Building Language\n";
1262 cout << "Version " << PEBL_VERSION << "\n";
1263 cout << "(c) 2003-2026 Shane T. Mueller, Ph.D.\n";
1264 cout << "smueller@obereed.net http://pebl.sf.net\n";
1265 cout << "-------------------------------------------------------------------------------\n";
1266
1267 cout << "Usage: Invoke pebl with the experiment script files (.pbl) and command-line\n";
1268 cout << "arguments.\n\n";
1269 cout << "example: pebl experiment.pbl -v sub1 --fullscreen --display 800x600 --driver opengl\n\n";
1270 cout << "COMMAND-LINE OPTIONS:\n";
1271 cout << "-v VALUE1 -v VALUE2\n";
1272 cout << " Invokes script and passes VALUE1 and VALUE2 (and any text immediately\n" ;
1273 cout << " following -v) to a list in the argument of the Start() function.\n\n";
1274 cout << "-s IDENTIFIER\n";
1275 cout << " Initiates the global variable gSubNum to IDENTIFIER. If not set here,\n";
1276 cout << " gSubNum is initiated to 0.\n\n";
1277 cout << "--driver <drivername>\n";
1278 cout << " Sets the preferred video driver via a hint, alternatives include direct3d opengl\n";
1279 cout << " opengles2 opengles and software\n";
1280 cout << "--display <widthxheight>\n";
1281 cout << " Controls the screen width and height (in pixels). Screen resolution defaults\n";
1282 cout << " to the current screen resolution. In fullscreen mode, PEBL will check whether \n";
1283 cout << " the resolution is available for the video screen, and use the default mode if not\n";
1284 cout << " Note: Custom screen dimensions can be controlled in-script.\n\n";
1285 cout << "--depth\n";
1286 cout << " Controls the pixel depth. Depends on your video card. Currently,\n";
1287 cout << " depths of 2,8,15,16,24, and 32 are allowed on the command-line.\n";
1288 cout << "--language <2 char lang code>\n";
1289 cout << " Allows you to specify at the command line a language to enable \n selecting different text labels.\n";
1290 cout << "--windowed\n";
1291 cout << "--fullscreen\n";
1292 cout << " Controls whether the script will run in a window or fullscreen.\n\n";
1293 cout << "--resizeable\n";
1294 cout << "--resizable\n";
1295 cout << " Controls whether the window will be resizeable (only in windowed mode)\n\n";
1296 cout << "--unicode\n";
1297 cout << " Turns on unicode handling, with slight overhead\n";
1298 cout << "--pfile <filename>\n";
1299 cout << " Specifies which parameter file to use, gets bound to variable gParamFile.\n";
1300 cout << " --upload <fname>\n";
1301 cout << " specifies an upload.json file to use to sync with data server\n";
1302 cout << " --vsyncon\n";
1303 cout << " Turns Vsync ON (for special tasks where you need precise control of video refresh, but may be tfussy on some hardware.\n";
1304
1305 cout << " --softrender\n";
1306 cout << " Uses software renderer instead of accelerated hardware fallback. Disables vsync setting\n";
1307
1308 cout << " --lsl [streamname]\n";
1309 cout << " Enables Lab Streaming Layer (LSL) output for EEG/eye-tracker synchronization.\n";
1310 cout << " Optional streamname overrides the auto-generated stream name (PEBL_<scriptname>).\n";
1311 cout << " Use InitializeLSL(), LSLMarker(), and FinalizeLSL() in your script.\n";
1312
1313 cout << " --help\n";
1314 cout << " Display this output screen\n";
1315
1316 cout << " --showtestresults\n";
1317 cout << " Sets global variable gShowTestResults to 1 vs 0. Allows a test to automatically show a screen with results at the end.\n";
1318
1319
1320}

References PEBL_VERSION.

Referenced by main(), and PEBLInterpret().

◆ SignalTestComplete()

void SignalTestComplete ( const char *  status = "completed")
inline

Definition at line 61 of file PEBL.cpp.

61 {
62 // Native PEBL doesn't need completion signaling
63}

Referenced by CaptureSignal(), and PEBLInterpret().

Variable Documentation

◆ head

PNode* head

Definition at line 220 of file PEBL.cpp.

Referenced by main(), PEBLInterpret(), and PEBLInterpret().

◆ myEnv

Definition at line 189 of file PEBL.cpp.

Referenced by CaptureSignal(), main(), PEBLInterpret(), and PlatformEventQueue::Prime().

◆ myEval

Evaluator* myEval = NULL

Definition at line 188 of file PEBL.cpp.

Referenced by PEBLInterpret().

◆ myLoader

Loader* myLoader

Definition at line 219 of file PEBL.cpp.

Referenced by CaptureSignal(), main(), PEBLInterpret(), and PEBLInterpret().