PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PEBL.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/apps/PEBL.cpp
4// Purpose: The primary PEBL run-time interpreter.
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#ifdef PEBL_WIN32
29// Include winsock2.h first to avoid conflicts with windows.h
30#include <winsock2.h>
31#endif
32
33#ifdef PEBL_ITERATIVE_EVAL
34#include "../base/Evaluator-es.h"
35#include "../devices/PEventLoop-es.h"
36#else
37#include "Globals.h"
38#include "../base/Evaluator.h"
39#include "../devices/PEventLoop.h"
40#endif
41
42#ifdef PEBL_EMSCRIPTEN
43#include <emscripten.h>
44#include <emscripten/html5.h>
45
46// Signal test completion to JavaScript launcher for test chains
47void SignalTestComplete(const char* status = "completed") {
48 EM_ASM({
49 var event = new CustomEvent('peblTestComplete', {
50 detail: {
51 status: UTF8ToString($0),
52 timestamp: Date.now()
53 }
54 });
55 document.dispatchEvent(event);
56 console.log('PEBL test completed with status:', UTF8ToString($0));
57 }, status);
58}
59#else
60// No-op on native builds
61inline void SignalTestComplete(const char* status = "completed") {
62 // Native PEBL doesn't need completion signaling
63}
64#endif
65
66#include "../base/grammar.tab.hpp"
67#include "../base/PNode.h"
68#include "../base/Loader.h"
69#include "../base/PComplexData.h"
70#include "../base/PList.h"
71#include "../base/FunctionMap.h"
72#include "../base/VariableMap.h"
73#include "../base/Variant.h"
74
75#include "../libs/PEBLObjects.h"
76
77#include "../utility/PError.h"
78#include "../utility/PEBLPath.h"
79#include "../utility/PEBLUtility.h"
80#include "../utility/rc_ptrs.h"
81#include "../utility/BinReloc.h"
82
83
84#include <iostream>
85#include <signal.h>
86#include <list>
87#include <string>
88#include <algorithm>
89#include <fstream>
90
91//Unix-specific definitions
92#if defined(PEBL_UNIX)
93//For running at higher priority:
94#include <sys/resource.h>
95//For better fifo scheduling.
96#include <sched.h>
97//For _exit():
98#include <unistd.h>
99
100#elif defined(PEBL_WIN32)
101//For running at higher priority.
102#include <winsock2.h> //avoid collision
103#include <windows.h>
104//For _exit():
105#include <process.h>
106
107STICKYKEYS g_StartupStickyKeys = {sizeof(STICKYKEYS), 0};
108TOGGLEKEYS g_StartupToggleKeys = {sizeof(TOGGLEKEYS), 0};
109FILTERKEYS g_StartupFilterKeys = {sizeof(FILTERKEYS), 0};
110
111
112
113
114void AllowAccessibilityShortcutKeys( bool bAllowKeys )
115{
116 if( bAllowKeys )
117 {
118 // Restore StickyKeys/etc to original state and enable Windows key
119 // STICKYKEYS sk = g_StartupStickyKeys;
120 // TOGGLEKEYS tk = g_StartupToggleKeys;
121 // FILTERKEYS fk = g_StartupFilterKeys;
122
123 SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &g_StartupStickyKeys, 0);
124 SystemParametersInfo(SPI_SETTOGGLEKEYS, sizeof(TOGGLEKEYS), &g_StartupToggleKeys, 0);
125 SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS), &g_StartupFilterKeys, 0);
126 }
127 else
128 {
129 // Disable StickyKeys/etc shortcuts but if the accessibility feature is on,
130 // then leave the settings alone as its probably being usefully used
131
132 STICKYKEYS skOff = g_StartupStickyKeys;
133 if( (skOff.dwFlags & SKF_STICKYKEYSON) == 0 )
134 {
135 // Disable the hotkey and the confirmation
136 skOff.dwFlags &= ~SKF_HOTKEYACTIVE;
137 skOff.dwFlags &= ~SKF_CONFIRMHOTKEY;
138
139 SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &skOff, 0);
140 }
141
142 TOGGLEKEYS tkOff = g_StartupToggleKeys;
143 if( (tkOff.dwFlags & TKF_TOGGLEKEYSON) == 0 )
144 {
145 // Disable the hotkey and the confirmation
146 tkOff.dwFlags &= ~TKF_HOTKEYACTIVE;
147 tkOff.dwFlags &= ~TKF_CONFIRMHOTKEY;
148
149 SystemParametersInfo(SPI_SETTOGGLEKEYS, sizeof(TOGGLEKEYS), &tkOff, 0);
150 }
151
152 FILTERKEYS fkOff = g_StartupFilterKeys;
153 if( (fkOff.dwFlags & FKF_FILTERKEYSON) == 0 )
154 {
155 // Disable the hotkey and the confirmation
156 fkOff.dwFlags &= ~FKF_HOTKEYACTIVE;
157 fkOff.dwFlags &= ~FKF_CONFIRMHOTKEY;
158
159 SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS), &fkOff, 0);
160 }
161 }
162}
163
164#endif
165
166#ifdef WIN32
167#include <time.h>
168#include <objbase.h>
169#include <shlobj.h>
170#endif
171
172
173
174#ifdef PEBL_OSX
175#include <mach-o/dyld.h> /* _NSGetExecutablePath */
176#include <CoreFoundation/CFBundle.h>
177#endif
178
179
180#ifdef PEBL_MOVIES
181#include "WAAVE.h"
182#endif
183
184#include "../platforms/sdl/PlatformEnvironment.h"
185#include "../platforms/sdl/SDLUtility.h"
186
187
188Evaluator * myEval = NULL;//new Evaluator(v,"Start");
190//myEval=NULL;//new Evaluator(v,"Start");
191//myEnv=NULL;
192
193
194using std::cerr;
195using std::endl;
196
197
203
204
205//Prototype for c function defined in grammar.y:
206PNode * parse(const char* filename);
207std::list<string> GetFiles(int argc, std::vector<std::string> argv);
208void PrintOptions();
209
217
218
221
222//std::list<PNode> PError::gCallStack;
223
224
225#ifdef PEBL_EMSCRIPTEN
226
227#endif
228
229int PEBLInterpret( int argc, std::vector<std::string> argv )
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}
929
930void CaptureSignal(int signal)
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}
966
967
968int main(int argc, char *argv[])
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}
1182
1183
1184//This returns a list of the files listed on the command-line.
1185std::list<std::string> GetFiles(int argc, std::vector<std::string> argv)
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}
1257
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}
char * br_find_prefix(const char *default_prefix)
Definition BinReloc.cpp:433
#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
char * br_find_exe(const char *default_exe)
Definition BinReloc.cpp:377
BrInitError
Definition BinReloc.h:22
@ BR_INIT_ERROR_DISABLED
Definition BinReloc.h:32
#define pInt
Definition Defs.h:8
PEBLVideoDepth
Definition Globals.h:69
PEBLVideoMode
Definition Globals.h:58
#define PEBL_VERSION
void SignalTestComplete(const char *status="completed")
Definition PEBL.cpp:61
int main(int argc, char *argv[])
Definition PEBL.cpp:968
void PrintOptions()
Definition PEBL.cpp:1258
PNode * head
Definition PEBL.cpp:220
PlatformEnvironment * myEnv
Definition PEBL.cpp:189
Loader * myLoader
Definition PEBL.cpp:219
int PEBLInterpret(int argc, std::vector< std::string > argv)
Definition PEBL.cpp:229
std::list< string > GetFiles(int argc, std::vector< std::string > argv)
Definition PEBL.cpp:1185
void CaptureSignal(int signal)
Definition PEBL.cpp:930
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 const PNode * gEvalNode
static PEBLPath gPath
bool Evaluate1()
static PCallStack gCallStack
static PEventLoop * mEventLoop
int GetNodeStackDepth()
static VariableMap gGlobalVariableMap
Variant Pop()
static FunctionMap mFunctionMap
Initiate some static member data.
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
void AddVariable(const std::string &varname, const Variant &val)
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
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)
const std::string StripFile(const std::string &file)
std::string GetFontForLanguageOrScript(const std::string &code, int fontType)
Variant GetWorkingDirectory()
PEBLVideoMode GetVideoMode(std::string modeline)
void SignalWarning(const std::string &message)
Definition PError.cpp:119
void SignalFatalError(const std::string &message)
Variant GetCurrentScreenResolution()