PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
sdl/PlatformWindow.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/platforms/sdl/PlatformWindow.cpp
4// Purpose: Contains SDL-specific interface for the main window class.
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2003-2026 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9//
10//
11// This file is part of the PEBL project.
12//
13// PEBL is free software; you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation; either version 2 of the License, or
16// (at your option) any later version.
17//
18// PEBL is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with PEBL; if not, write to the Free Software
25// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27#include "PlatformWindow.h"
28#include "PlatformEnvironment.h"
29
30#include "../../objects/PWindow.h"
31#include "../../objects/PColor.h"
32#include "../../base/Evaluator.h"
33#include "../../base/VariableMap.h"
34#include "../../base/PComplexData.h"
35#include "../../libs/PEBLEnvironment.h"
36#include "SDLUtility.h"
37
38#ifdef PEBL_EMSCRIPTEN
39#include "emscripten.h"
40#endif
41
42
43
44#include "SDL.h"
45#include <stdio.h>
46
47// cout removed - use cerr for debug output
48using std::cerr;
49using std::endl;
50using std::flush;
51using std::list;
52using std::ostream;
53
54
57 mFlags(0),
58 mWindow(NULL),
59 PWindow(penv)
60
61{
63 // InitializeProperty("COLOR",Variant("grey"));//dummy property setting
64 // PWidget::SetBackgroundColor(PColor(200,200,200,255));
65 PColor tmpColor = PColor(0,0,0,255);
67
68 //These don't get used for windows:
69 mSurface = NULL;
70 mTexture = NULL;
72}
73
76{
77
78
79 SDL_DestroyWindow(mWindow);
80 mWindow = NULL;
81
82}
83
84// Inheritable function that is called by friend method << operator of PComplexData
85ostream & PlatformWindow::SendToStream(ostream& out) const
86{
87 out << "<SDL PlatformWindow>" << flush;
88 return out;
89}
90
91//Hopefully this won't be needed in SDL2
93{
94 return 0;
95
96}
97
98
99
100
103 PEBLVideoDepth vdepth,
104 bool windowed,
105 bool resizeable,
106 unsigned int width,
107 unsigned int height)
108{
109
110 cerr << "Initializing " << width << "--" << height << endl;
111 SDLUtility::GetDriverList(); //print out driver information.
112 Variant v = 0;
113
114 Evaluator * myEval = new Evaluator(v,"Window");
115 //gVideoWidth and gVideoHeight may have been set by the user
116 //in the script. First, get these values, and try them.
117
118 Variant vDp = 0;
119 bool vsync = true;
120 bool softrender = false;
121
122
123
124
125 if(myEval->gGlobalVariableMap.Exists("gVideoDepth"))
126 vDp = myEval->gGlobalVariableMap.RetrieveValue("gVideoDepth");
127
128
129 if(myEval->gGlobalVariableMap.Exists("gVSync"))
130 {
131 vsync = (int)((myEval->gGlobalVariableMap.RetrieveValue("gVsync"))) != 0;
132
133 }
134 //cout <<" vsync state: " << vsync <<"\n";
135
136 if(myEval->gGlobalVariableMap.Exists("gSoftRender"))
137 {
138 softrender = (int)((myEval->gGlobalVariableMap.RetrieveValue("gSoftRender"))) != 0;
139 }
140
141
142
143
144 //Should be direct3d (windows), opengl, opengles2 opengles,software.
145 if(myEval->gGlobalVariableMap.Exists("gDriverHint"))
146 {
147 SDL_SetHint("SDL_HINT_RENDER_DRIVER",
148 ( myEval->gGlobalVariableMap.RetrieveValue("gDriverHint").GetString().c_str()));
149 }
150
151
152 //some other hints available via SDL
153 //SDL_SetHint("SDL_HINT_VIDEO_ALLOW_SCREENSAVER","0");
154 if(!windowed)
155 {
156 //this might not be the best approach.
157 SDL_SetHint("SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES","1");
158 }
159
160
161 if(vsync)
162 {
163 SDL_SetHint("SDL_HINT_RENDER_VSYNC","1");
164 }
165
166
167 SDL_SetHint("SDL_HINT_MOUSE_RELATIVE_MODE_WARP","1");
168
169 //Set the renderer flags. We are by default accelerated.
170 //there we could bump up to vsync, or down to software.
171 //If you try to specify both vsync and software, it will just use software.
172
173 int rflags = SDL_RENDERER_TARGETTEXTURE;
174 if(softrender)
175 {
176 rflags |= SDL_RENDERER_SOFTWARE;
177 }else
178 {
179
180 rflags |= SDL_RENDERER_ACCELERATED;
181
182#ifndef PEBL_EMSCRIPTEN
183 if(vsync)
184 {
185 rflags |= SDL_RENDERER_PRESENTVSYNC;
186
187 }
188#endif
189 }
190
191
192 int depth = vDp;
193
194 // cout << width << "----" << height << " in platformwindow\n";
195
196
197
198 if(!(depth > 0))
199 depth = (int)vdepth;
200
201
202
203 int vflags = 0;//GetVideoFlags();
204
205 if(!windowed)
206 {
207 vflags |= SDL_WINDOW_FULLSCREEN ; // Enable fullscreen
208 }
209
210 if(resizeable)
211 {
212 vflags |= SDL_WINDOW_RESIZABLE; //IS the window resizeable?
213 }
214
215 //Re-store the values
216 myEval->gGlobalVariableMap.AddVariable("gVideoWidth", (long unsigned int)width);
217 myEval->gGlobalVariableMap.AddVariable("gVideoHeight", (long unsigned int)height);
218 myEval->gGlobalVariableMap.AddVariable("gVideoDepth", depth);
219
220 PWidget::SetProperty("WIDTH",(long unsigned int)width);
221 PWidget::SetProperty("HEIGHT",(long unsigned int)height);
222
223 delete myEval;
224
225 bool success = 0;
226 //INitialize the SDL surface with the appropriate flags.
227
228 Variant scriptname = "PEBL Application";
229 if(myEval->gGlobalVariableMap.Exists("gScriptName"))
230 {
231 scriptname = myEval->gGlobalVariableMap.RetrieveValue("gScriptName");
232 }
233
234 std::string sname = scriptname;
235 int x= SDL_WINDOWPOS_CENTERED;
236 int y= 15;//SDL_WINDOWPOS_CENTERED;
237 if(!windowed)
238 {
239 x = SDL_WINDOWPOS_UNDEFINED;
240 y = SDL_WINDOWPOS_UNDEFINED;
241 }
242
243 mWindow =SDL_CreateWindow(sname.c_str(),x,y,
244 //SDL_WINDOWPOS_UNDEFINED,
245 //SDL_WINDOWPOS_UNDEFINED,
246 width,height, vflags);
247
249
250 mRenderer = SDL_CreateRenderer(mWindow, -1,rflags);
251// mRenderer = SDL_CreateRenderer(mWindow,-1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE|SDL_RENDERER_PRESENTVSYNC);
252 //mRenderer = SDL_CreateSoftwareRenderer(SDL_GetWindowSurface(mWindow));
253 //mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_TARGETTEXTURE);
254
255 if(mRenderer)
256 {
257 success = true;
258 }else{
259 PError::SignalFatalError("Failed to create renderer in PlatformWindow\n");
260 }
261
262
263 mFlags = vflags;
264#ifdef SDL2_DELETE
265 // mSurface = SDL_SetVideoMode(width,height,depth,SDL_HWACCEL | SDL_FULLSCREEN | SDL_DOUBLEBUF);
266 if ( mSurface == NULL )
267 {
268 //If we fail, try to do our best.
269 cerr << "Unable to set " << width << "x" << height << ": " << depth << " video mode: " << SDL_GetError() << endl;
270
271 //This needs to be reworked because of new API that handles multi-screens, etc.
272
273 //Get a list of available resolutions and try these out.
274 SDL_Rect** modes = SDL_ListModes(NULL,vflags);
275 if(modes == (SDL_Rect**)0)
276 {
277 cerr << "No Video Modes Available" << endl;
278 } else{
279 for(int i=0; modes[i];++i)
280 {
281 width=modes[i]->w;
282 height=modes[i]->h;
283
284
285 cerr << "Trying resolution:" << width << "x" << height << ": " << depth << " video mode:\n ";
286 mSurface = SDL_SetVideoMode(width,height,depth,vflags);
287 if(mSurface)
288 {
289 cerr << "Using resolution:" << width << "x" << height << ": " << depth << " video mode:\n ";
290
291 success = true;
292 break;
293 }else{
294 cerr << "..........Resolution failed\n";
295 }
296 }
297
298 cerr<< "Resolution may not be optimal. Alternate resolutions available on system include:\n";
299
300 modes =SDL_ListModes(NULL,vflags);
301 for(int ii=0; modes[ii]; ++ii)
302 {
303 cerr << ii<< ". gVideoWidth <-"<< modes[ii]->w<<"; gVideoHeight<- " << modes[ii]->h <<"\n";
304 }
305}
306
307
308
309
310 if(success)
311 {
312 //something worked out!
313 myEval->gGlobalVariableMap.AddVariable("gVideoWidth", width);
314 myEval->gGlobalVariableMap.AddVariable("gVideoHeight", height);
315 myEval->gGlobalVariableMap.AddVariable("gVideoDepth", depth);
316 }else{
317
318 //None of the resolutions worked out.
319 return false;
320 }
321
322
323 }else{
324 success = true;
325 }
326#endif
327 if(success)
328 {
329 cerr << "\n\n--------------------------------------------------------------------------------\n";
330 cerr << " Application settings:" << endl;
331
332 cerr << "Script name: [" << sname << "]\n";
333 cerr << PEBLEnvironment::TimeStamp(Variant(0)) << endl;
334 cerr << "--------------------------------------------------------------------------------\n";
335
336 cerr << "Display Mode: Width (pixels) [" << width << "]\n";
337 cerr << "Display Mode: Height (pixels) [" << height << "]\n";
338 cerr << "Display Mode: Color Depth (bits) [" << depth << "]\n";
339 cerr << "vsync mode: [" << vsync << "]\n";
340 cerr << "Software renderer mode: [" <<softrender << "]\n";
341 cerr << "Windowed: ["<<windowed<< "]\n";
342 cerr << "Resizeable: ["<<resizeable<< "]\n";
343
344 cerr << "Driver hint (gDriverHint): ";
345
346 if(myEval->gGlobalVariableMap.Exists("gDriverHint"))
347 {
348
349 cerr << "["<<myEval->gGlobalVariableMap.RetrieveValue("gDriverHint") << "]\n";
350 }
351 else
352 {
353 cerr << "[none]\n";
354 }
355
356
357
358 cerr << "Base font (gPEBLBaseFont): ["<<
359 myEval->gGlobalVariableMap.RetrieveValue("gPEBLBaseFont") << "]\n";
360
361 cerr << "Base Mono font (gPEBLBaseFontMono): ["<<
362 myEval->gGlobalVariableMap.RetrieveValue("gPEBLBaseFontMono") << "]\n";
363
364 cerr << "Base serif (gPEBLBaseFontSerif): ["<<
365 myEval->gGlobalVariableMap.RetrieveValue("gPEBLBaseFontSerif") << "]\n";
366
367 cerr << "Language (gLanguage): ["<<
368 myEval->gGlobalVariableMap.RetrieveValue("gLanguage") << "]\n";
369
370 cerr << "Parameter file (gParamFile): ["<<
371 myEval->gGlobalVariableMap.RetrieveValue("gParamFile") << "]\n";
372
373 cerr << "Busy/Easy wait: (gSleepEasy): ["<<
374 myEval->gGlobalVariableMap.RetrieveValue("gSleepEasy") << "]\n";
375
376
377 cerr << "Executable name: (gExecutableName): ["<<
378 myEval->gGlobalVariableMap.RetrieveValue("gExecutableName") << "]\n";
379
380
381 cerr << "Resource path: (gPEBLResourcePath): [" <<
382 myEval->gGlobalVariableMap.RetrieveValue("gPEBLResourcePath") << "]\n";
383
384
385 cerr << "Resource path: (gPEBLBasePath): [" <<
386 myEval->gGlobalVariableMap.RetrieveValue("gPEBLBasePath") << "]\n";
387
388 cerr << "Working directory: (gWorkingDirectory): [" <<
389 myEval->gGlobalVariableMap.RetrieveValue("gWorkingDirectory") << "]\n";
390
391
392
393 SDL_RendererInfo drinfo;
394 cerr << "Renderer information:\n";
395 SDL_GetRendererInfo(mRenderer, &drinfo);
396 cerr << "Driver name: [" << drinfo.name << "]\n";;
397
398 std::cerr << "Software fallback: [";
399
400 if (drinfo.flags & SDL_RENDERER_SOFTWARE)
401 std::cerr << "yes]\n"; else std::cerr << "no]\n";
402
403 std::cerr << "Hardware acceleration: [";
404
405 if (drinfo.flags & SDL_RENDERER_ACCELERATED)
406 std::cerr << "yes]\n"; else std::cerr << "no]\n";
407
408 std::cerr << "Vsync with refresh rate: [";
409 if (drinfo.flags & SDL_RENDERER_PRESENTVSYNC)
410 std::cerr << "yes]\n"; else std::cerr << "no]\n";
411
412 std::cerr << "Rendering to texture support [";
413
414 if (drinfo.flags & SDL_RENDERER_TARGETTEXTURE)
415 std::cerr << "yes]\n"; else std::cerr << "no]\n";
416
417 cerr << "--------------------------------------------------------------------------------\n\n";
418
419 //Do an initial Draw()
420 Draw();
421 return true;
422 }
423 return false;
424}
425
426
428{
429
430
431
432 //if (SDL_MUSTLOCK(mSurface)) SDL_LockSurface(mSurface);
433
434
435 // Get background color from property system (in case it was modified via nested properties)
436 Variant backgroundColor = PEBLObjectBase::GetProperty("BGCOLOR");
437 PColor* bgColor = nullptr;
438 if(backgroundColor.GetComplexData())
439 {
440 bgColor = dynamic_cast<PColor*>(backgroundColor.GetComplexData()->GetObject().get());
441 }
442
443 // Use property color if available, otherwise fall back to mBackgroundColor
444 if(!bgColor)
445 {
446 bgColor = &mBackgroundColor;
447 }
448
449#ifdef SDL2_DELETE
450 //First, draw the background
451 SDL_FillRect(mSurface, NULL, SDL_MapRGBA(mSurface->format,
452 bgColor->GetRed(),
453 bgColor->GetGreen(),
454 bgColor->GetBlue(),
455 bgColor->GetAlpha()));
456#endif
457
458
459 int result= SDL_SetRenderDrawColor(mRenderer,
460 bgColor->GetRed(),
461 bgColor->GetGreen(),
462 bgColor->GetBlue(),
463 bgColor->GetAlpha());
464
465
466 // while(p != mSubWidgets.begin())
467 // {
468 // p--;
469 //cout << "------drawing Subwidget on window:" << *p << "[" << **p << "]" << endl;
470
471 // }
472
473 SDL_RenderClear(mRenderer);//clear the window
474 if(result < 0)
475 {
476 PError::SignalFatalError("Unable to clear window");
477 std::cerr <<SDL_GetError() << endl;
478 }
479
480
481 //cout << "Number of subwidgets: " << mSubWidgets.size() << endl;
482 //This handles drawing each subwidget.
483 std::list<PWidget *>::iterator p = mSubWidgets.end();
484
485 while(p != mSubWidgets.begin())
486 {
487 //cout <<"Size"<< mSubWidgets.size() << endl;
488
489
490 //decrement iterator--moving backward so we draw things in
491 //reverse order.
492 p--;
493
494 //Draw the subwidget
495 if((*p)->IsVisible())
496 {
497 (*p)->Draw();
498 }
499
500 }
501
502 SDL_RenderPresent(mRenderer);//draw the window.
503
504
505#ifdef PEBL_EMSCRIPTEN
506 // int result = SDL_Flip(mSurface);
507#else
508// int result = SDL_Flip(mSurface);
509#endif
510
511
512 return true;
513}
514
515// This will draw the current screen repeatedly for cycles cycles and return.
516// On platforms where SDL_Flip blocks until it finishes (e.g., maybe like linux),
517// then it should return immediately after the vsync, allowing time for removing the
518// surface or whatever. On platforms where SDL_Flip returns immediately, it should
519// still block until the _next_ cycle, so it will return right after the n-1 cycle, giving
520// time to delete etc before another draw command needs to be issued.
521long int PlatformWindow::DrawFor(unsigned int cycles)
522{
523
524 //Do an initial syncing flip.
525 int result = 0;// = SDL_Flip(mSurface);
526
527 // cout << "Drawforing cycles: " << cycles << endl;
528
529 //Now, draw the subwidgets.
530
531 unsigned int cyclesleft = cycles;
532 unsigned int tstart = SDL_GetTicks();
533
534 while(cyclesleft > 0 )
535 {
536 //Get the time *AFTER* the first cycle.
537 //This method will only really work on a platform wher SDL_Flip blocks.
538
539 //result = SDL_Flip(mSurface);
540 Draw();
541 // cout << ".";
542 cyclesleft--;
543 }
544
545 unsigned int tend = SDL_GetTicks();
546 //cout << "\n";
547 result = tend-tstart;
548
549 //If the return value is positive, it is the time that
550 //this was displayed. Otherwise, it is an error code.
551 return result;
552
553}
554
555bool PlatformWindow::Resize(int w, int h)
556{
557 //Resize the screen
558
559 // screen = SDL_SetVideoMode( event.resize.w, event.resize.h, SCREEN_BPP, SDL_SWSURFACE | SDL_RESIZABLE );
560 Variant vDp = myEval->gGlobalVariableMap.RetrieveValue("gVideoDepth");
561
562//rework this for SDL2:
563#ifdef SDL2_DELETE
564 mSurface = SDL_SetVideoMode(w,h,(int)vDp,mFlags);
565
566 if(mSurface)
567 {
568 myEval->gGlobalVariableMap.AddVariable("gVideoWidth", w);
569 myEval->gGlobalVariableMap.AddVariable("gVideoHeight", h);
570 }
571
572#endif
573 return true;
574}
575
576
577
578int PlatformWindow::SaveScreenShot(int x, int y, int w, int h,const Variant fname)
579{
580
581 SDL_Rect rect;
582 rect.x = x;
583 rect.y = y;
584 rect.w = w;
585 rect.h = h;
586
587 return SDLUtility::WritePNG(mRenderer,&rect,fname);
588
589
590}
#define NULL
Definition BinReloc.cpp:317
PEBLVideoDepth
Definition Globals.h:69
PEBLVideoMode
Definition Globals.h:58
Evaluator * myEval
Definition PEBL.cpp:188
@ CDT_WINDOW
Definition PEBLObject.h:58
This class has got everything you need to evaluate stuff.
static VariableMap gGlobalVariableMap
int GetRed() const
Definition PColor.cpp:226
int GetAlpha() const
Definition PColor.cpp:229
int GetBlue() const
Definition PColor.cpp:228
int GetGreen() const
Definition PColor.cpp:227
counted_ptr< PEBLObjectBase > GetObject() const
ComplexDataType mCDT
Definition PEBLObject.h:109
Variant GetProperty(std::string) const
virtual void SetBackgroundColor(const PColor &color)
Definition PWidget.cpp:287
virtual bool SetProperty(std::string, Variant v)
Definition PWidget.cpp:142
std::list< PWidget * > mSubWidgets
Definition PWidget.h:147
PColor mBackgroundColor
The background color of the widget. if alpha = 0, will not be painted.
Definition PWidget.h:144
SDL_Surface * mSurface
SDL_Texture * mTexture
SDL_Renderer * mRenderer
virtual std::ostream & SendToStream(std::ostream &out) const
An inheritable printing class used by PEBLObjectBase::operator<<.
PlatformWindow(PlatformEnvironment *penv)
Standard Constructor.
bool Resize(int w, int h)
int SaveScreenShot(int x, int y, int w, int h, const Variant fname)
bool Initialize(PEBLVideoMode mode, PEBLVideoDepth vdepth, bool windowed, bool resizeable, unsigned int width, unsigned int height)
This method initiates everything needed to display the main window.
long int DrawFor(unsigned int cycles)
~PlatformWindow()
Standard Destructor.
bool Exists(const std::string &varname)
void AddVariable(const std::string &varname, const Variant &val)
Variant RetrieveValue(const std::string &varname)
std::string GetString() const
Definition Variant.cpp:1056
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
X * get() const
Definition rc_ptrs.h:110
Variant TimeStamp(Variant v)
void SignalFatalError(const std::string &message)
Variant GetDriverList(bool printout=true)
int WritePNG(SDL_Renderer *renderer, SDL_Rect *rect, const std::string fname)