PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
sdl/PlatformEnvironment.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/PlatformEnvironment.cpp
4// Purpose: Contains SDL-specific interface for the main environment.
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#if defined PEBL_WIN32
28#include <winsock2.h> // Must be first to avoid conflicts
29#endif
30
31#include "PlatformEnvironment.h"
32
33#include "../../objects/PEnvironment.h"
34#include "PlatformWindow.h"
35#include "../../base/PList.h"
36#include "../../base/PComplexData.h"
37
38#include <fstream>
39#include <iostream>
40#include <algorithm>
41
42#include <list>
43#include <stdio.h>
44
45#include "../../utility/PError.h"
46#include "../../utility/PEBLUtility.h"
47
48#if defined PEBL_WIN32
49#include <windows.h>
50//#include <ddraw.h> This may be needed for compiling with VC++
51#endif
52
53// cout removed - use cerr for debug output
54using std::cerr;
55using std::endl;
56using std::flush;
57using std::list;
58using std::ostream;
59
60
63 bool windowed,bool resizeable,bool unicode):
65 mVideoMode(mode),
66 mVideoDepth(depth),
67 mWindowed(windowed),
68 mUnicode(unicode),
69 mResizeable(resizeable),
70 mNumJoysticks(0)
71{
72 mIsInitialized = false;
73}
74
75
78{
79 mWindows.clear();
80
81 // SDL_Quit(); //this can cause a crash; SDL_Quit needs to be called at very end.
82 // especially if there are global SDL objects like gWin remaining.
83 //probably need to explicitly delete the globalvariable map;for now SDL_quit gets
84 //called essentially in main().
85}
86
87
88// Inheritable function that is called by friend method << operator of PComplexData
89ostream & PlatformEnvironment::SendToStream(ostream& out) const
90{
91
92 out << "<SDL PlatformEnvironment>" << flush;
93 return out;
94}
95
96
97
100{
101
102 mIsInitialized = true;
103
104#ifdef PEBL_EMSCRIPTEN
105#define SDLINIT_FLAGS SDL_INIT_VIDEO
106#else
107#define SDLINIT_FLAGS SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE
108#endif
109 if ( SDL_Init(SDLINIT_FLAGS) < 0 )
110 {
111
112 cerr << "Unable to init SDL: " << SDL_GetError() << endl;;
113 mIsInitialized = false;
114 }
115 else{
116
117 std::cerr << "SDL INITIALIZED\n";
118 const char* audioDriver = SDL_GetCurrentAudioDriver();
119 if(audioDriver) {
120 std::cerr << "SDL Audio Driver: " << audioDriver << "\n";
121 } else {
122 std::cerr << "SDL Audio Driver: NONE\n";
123 }
124
125#ifdef PEBL_EMSCRIPTEN
126 if (TTF_Init() < 0 )
127#else
128 if (!TTF_WasInit() && TTF_Init() < 0 )
129#endif
130 {
131 /* Initialize the TTF library */
132 cerr << "Couldn't initialize TTF: " << SDL_GetError() << endl;
133 mIsInitialized = false;
134 } else
135 {
136 std::cerr << "TTF INITIALIZED\n";
137 }
138
139 // Initialize SDL_image for JPEG, PNG, etc.
140#ifdef PEBL_EMSCRIPTEN
141 // Emscripten: formats specified at compile time via SDL2_IMAGE_FORMATS
142 int imgFlags = IMG_INIT_JPG | IMG_INIT_PNG;
143#else
144 int imgFlags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF;
145#endif
146 cerr << "Attempting to initialize SDL_image with flags: " << imgFlags << endl;
147 cerr << " IMG_INIT_JPG = " << IMG_INIT_JPG << endl;
148 cerr << " IMG_INIT_PNG = " << IMG_INIT_PNG << endl;
149
150 int imgInitResult = IMG_Init(imgFlags);
151 cerr << "IMG_Init returned: " << imgInitResult << endl;
152
153 if ((imgInitResult & imgFlags) != imgFlags)
154 {
155 cerr << "WARNING: SDL_image could not initialize all requested formats!" << endl;
156 cerr << " Requested flags: " << imgFlags << endl;
157 cerr << " Initialized flags: " << imgInitResult << endl;
158 cerr << " IMG_Error: " << IMG_GetError() << endl;
159
160 if (!(imgInitResult & IMG_INIT_JPG)) {
161 cerr << " JPEG support NOT available!" << endl;
162 }
163 if (!(imgInitResult & IMG_INIT_PNG)) {
164 cerr << " PNG support NOT available!" << endl;
165 }
166 } else
167 {
168 std::cerr << "SDL_image INITIALIZED successfully with all formats" << endl;
169 std::cerr << " JPEG support: YES" << endl;
170 std::cerr << " PNG support: YES" << endl;
171 }
172
173 // Pre-initialize keycode lookup tables to avoid timing artifacts on first keypress
174 // This is critical for psychological experiments with accurate response time measurements
176
177#define PRINTCONSOLE 0
178#if PRINTCONSOLE
179 //cout << "reopening console\n";
180 //This needs to be done after SDL_Init()
181 std::ofstream cct1("CON1");
182 std::ofstream cct2("CON2");
183
184 freopen( "CON1", "w", stdout );
185 freopen( "CON2", "w", stderr );
186#endif
187
188 }
189 //This shouldn't be neded, if platformenvironment gets cleaned up alright,
190 //but let's do it anyway.
191 //atexit(SDL_Quit);
192 // std::cerr << "SDL INITIALIZATION COMPLETE\n";
193}
194
195
196
200{
201 //Iterate through all child windows and draw them
202 list<PWindow*>::iterator p;
203 for(p=mWindows.begin(); p!= mWindows.end(); p++)
204 {
205
206
207 if(*p)
208 (*p)->Draw();
209 }
210
211 return true;
212}
213
214
215//overloaded generic PEBLObjectBase methods
217{
218 //this class has no settable properties.
219 return false;
220}
221
223{
224 return PEBLObjectBase::GetProperty(name);
225}
226
227// ObjectValidationError PlatformEnvironment::ValidateProperty(std::string, Variant v)const
228// ObjectValidationError PlatformEnvironment::ValidateProperty(std::string)const;
229
231{
232
233 if(val)
234 {
235 return SDL_ShowCursor(SDL_ENABLE);
236 }
237 else
238 {
239 return SDL_ShowCursor(SDL_DISABLE);
240 }
241}
242
243
245{
246 //this will move the mouse in the first window.
247 //another method is necessary to move it within an arbitrary window.
248 PlatformWindow* pw =dynamic_cast<PlatformWindow*>( mWindows.front());
249
250 SDL_SetRelativeMouseMode(SDL_FALSE);
251 //cout << "Warping mouse:" <<x << "|" << y<< std::endl;
252 SDL_Window *win = pw->PlatformWindow::GetSDLWindow();
253 SDL_WarpMouseInWindow(win,x,y);
254 //printf("SDL_warp error?: %s\n", SDL_GetError());
255
256 return 1;
257}
258
259
261{
262 int x = 0;
263 int y = 0;
264 SDL_PumpEvents();
265 SDL_GetMouseState(&x,&y);
266
267 PList * newlist = new PList();
268 newlist->PushBack(Variant(x));
269 newlist->PushBack(Variant(y));
271 PComplexData * pcd = new PComplexData(newlist2);
272 return Variant(pcd);
273}
274
275
277{
278 //This is no longer available in SDL2
279#ifdef SDL2_DELETE
280 if(onoff)
281 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
282 else
283 SDL_EnableKeyRepeat(0,0);
284#endif
285}
286
288{
289 int x = 0;
290 int y = 0;
291 SDL_PumpEvents();
292 int button = SDL_GetMouseState(&x,&y);
293 int b1 = button & SDL_BUTTON(1);
294 int b2 = button & SDL_BUTTON(2);
295 int b3 = button & SDL_BUTTON(3);
296
297 PList * newlist = new PList();
298 newlist->PushBack(Variant(x));
299 newlist->PushBack(Variant(y));
300 newlist->PushBack(Variant(b1));
301 newlist->PushBack(Variant(b2));
302 newlist->PushBack(Variant(b3));
303
305 PComplexData * pcd = new PComplexData(newlist2);
306 return Variant(pcd);
307}
308
309
310// This will initialize the joystick subsystem
311// and return the number of available joysticks.
313{
314 //Initialize joystick subsystem if has not yet been.
315 if(SDL_InitSubSystem(SDL_INIT_JOYSTICK)<0)
316 {
317 PError::SignalWarning("No joysticks available");
318 }
319 int num = SDL_NumJoysticks();
320
321 return num;
322}
323
324
325
326
327// This will initialize the joystick subsystem
328// and return the number of available joysticks.
330{
331
332 if(index < 1 || index > SDL_NumJoysticks())
333 {
334 PError::SignalFatalError("Requesting an invalid joystick");
335 }
336 // SDL_Joystick * joystick = SDL_JoystickOpen(int index);
337 PlatformJoystick* joystick = new PlatformJoystick(index+1);
338
339
340
342
343 PComplexData * pcd = new PComplexData(tmp);
344 return Variant(pcd);
345
346}
347
348//
350{
351
352
353 int low,hi;
354
355 int numdisplays = SDL_GetNumVideoDisplays();
356
357 if(screen<0 | screen >=numdisplays)
358 {
359 low=0;
360 hi = numdisplays;
361 }
362 else
363 {
364 low = screen;
365 hi = screen+1;
366 }
367
368
369
370 // Uint32 f;
371 PList * baselist = new PList();
372 PComplexData * pcd=NULL;
373
374
375 //Get a list of available resolutions and try these out.
376
377
378 int nummodes = 0;
379 int totalmodes = 0;
380 SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
381 for(int i = low; i < hi; i++) //go through each screen specified.
382 {
383 nummodes = SDL_GetNumDisplayModes(i); //We should query each display here.
384 totalmodes += nummodes;
385
386 for(int modeid = 0;modeid < nummodes; modeid++)
387 {
388
389 SDL_GetDisplayMode(i,modeid,&mode);
390
391 PList * newlist = new PList();
392
393 newlist->PushBack(Variant(mode.w));
394 newlist->PushBack(Variant(mode.h));
395 newlist->PushBack(Variant(i)); //add screen ID
396 newlist->PushBack(Variant(mode.refresh_rate));
397
399 pcd = new PComplexData(newlist2);
400 baselist->PushBack(Variant(pcd));
401
402 }
403 }
404
405
407 pcd = new PComplexData(baselist2);
408 return Variant(pcd);
409}
410
411// Get the system locale from OS settings
412// Uses SDL_GetPreferredLocales() to query OS for user's preferred language/locale
413// Returns locale string like "ar", "en_US", "zh_CN", "he_IL"
414// Returns empty string on error
416 SDL_Locale *locales = SDL_GetPreferredLocales();
417 if (!locales) {
418 return ""; // Error or not supported on this platform
419 }
420
421 // Get the first (primary) locale
422 std::string result = "";
423 if (locales[0].language) {
424 result = locales[0].language;
425
426 // Append country code if available (e.g., "en_US", "zh_CN")
427 if (locales[0].country) {
428 result += "_";
429 result += locales[0].country;
430 }
431 }
432
433 SDL_free(locales);
434 return result; // e.g., "ar", "en_US", "zh_CN", "he_IL", "ko_KR"
435}
436
437// Check if the system locale is RTL (Arabic, Hebrew)
438// Useful for setting default text box justification before any text input
440 std::string locale = GetSystemLocale();
441 if (locale.empty()) {
442 return false; // Default to LTR if we can't detect
443 }
444
445 // Extract language code (first 2 characters)
446 std::string langCode = locale.substr(0, 2);
447 std::transform(langCode.begin(), langCode.end(), langCode.begin(), ::tolower);
448
449 // Check if it's Arabic or Hebrew
450 return (langCode == "ar" || langCode == "he" || langCode == "iw");
451}
452
453
454
#define NULL
Definition BinReloc.cpp:317
PEBLVideoDepth
Definition Globals.h:69
PEBLVideoMode
Definition Globals.h:58
@ CDT_ENVIRONMENT
Definition PEBLObject.h:50
Variant GetProperty(std::string) const
bool mIsInitialized
std::list< PWindow * > mWindows
Definition PList.h:45
void PushBack(const Variant &v)
Definition PList.cpp:149
virtual bool SetProperty(std::string, Variant v)
virtual Variant GetJoystick(int index)
virtual ~PlatformEnvironment()
Standard Destructor.
virtual int SetCursorPosition(int x, int y)
virtual std::ostream & SendToStream(std::ostream &out) const
virtual std::string GetSystemLocale()
virtual void SetKeyRepeat(bool onoff)
Variant GetScreenModes(int screen=-1)
virtual int ShowCursor(int val)
virtual Variant GetProperty(std::string) const
void Initialize()
This method initiates everything needed to display the main window.
PlatformEnvironment(PEBLVideoMode mode, PEBLVideoDepth depth, bool windowed, bool resizeable, bool unicode)
Standard Constructor.
virtual Variant GetCursorPosition()
void InitializeKeycodeLookups()
void SignalWarning(const std::string &message)
Definition PError.cpp:119
void SignalFatalError(const std::string &message)
#define SDLINIT_FLAGS