1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #define FORBIDDEN_SYMBOL_ALLOW_ALL
24 
25 #include "backends/platform/sdl/sdl.h"
26 #include "common/config-manager.h"
27 #include "gui/EventRecorder.h"
28 #include "common/taskbar.h"
29 #include "common/textconsole.h"
30 #include "common/translation.h"
31 #include "common/encoding.h"
32 
33 #include "backends/saves/default/default-saves.h"
34 
35 // Audio CD support was removed with SDL 2.0
36 #if SDL_VERSION_ATLEAST(2, 0, 0)
37 #include "backends/audiocd/default/default-audiocd.h"
38 #else
39 #include "backends/audiocd/sdl/sdl-audiocd.h"
40 #endif
41 
42 #include "backends/events/default/default-events.h"
43 #include "backends/events/sdl/sdl-events.h"
44 #include "backends/mutex/sdl/sdl-mutex.h"
45 #include "backends/timer/sdl/sdl-timer.h"
46 #include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
47 #ifdef USE_OPENGL
48 #include "backends/graphics/openglsdl/openglsdl-graphics.h"
49 #include "graphics/cursorman.h"
50 #endif
51 
52 #include <time.h>	// for getTimeAndDate()
53 
54 #ifdef USE_DETECTLANG
55 #ifndef WIN32
56 #include <locale.h>
57 #endif // !WIN32
58 #endif
59 
60 #ifdef USE_SDL_NET
61 #include <SDL_net.h>
62 #endif
63 
64 #if SDL_VERSION_ATLEAST(2, 0, 0)
65 #include <SDL_clipboard.h>
66 #endif
67 
OSystem_SDL()68 OSystem_SDL::OSystem_SDL()
69 	:
70 #ifdef USE_OPENGL
71 	_desktopWidth(0),
72 	_desktopHeight(0),
73 	_graphicsModes(),
74 	_graphicsMode(0),
75 	_firstGLMode(0),
76 	_defaultSDLMode(0),
77 	_defaultGLMode(0),
78 #endif
79 	_inited(false),
80 	_initedSDL(false),
81 #ifdef USE_SDL_NET
82 	_initedSDLnet(false),
83 #endif
84 	_logger(0),
85 	_mixerManager(0),
86 	_eventSource(0),
87 	_window(0) {
88 
89 	ConfMan.registerDefault("kbdmouse_speed", 3);
90 	ConfMan.registerDefault("joystick_deadzone", 3);
91 }
92 
~OSystem_SDL()93 OSystem_SDL::~OSystem_SDL() {
94 	SDL_ShowCursor(SDL_ENABLE);
95 
96 	// Delete the various managers here. Note that the ModularBackend
97 	// destructor would also take care of this for us. However, various
98 	// of our managers must be deleted *before* we call SDL_Quit().
99 	// Hence, we perform the destruction on our own.
100 	delete _savefileManager;
101 	_savefileManager = 0;
102 	if (_graphicsManager) {
103 		dynamic_cast<SdlGraphicsManager *>(_graphicsManager)->deactivateManager();
104 	}
105 	delete _graphicsManager;
106 	_graphicsManager = 0;
107 	delete _window;
108 	_window = 0;
109 	delete _eventManager;
110 	_eventManager = 0;
111 	delete _eventSource;
112 	_eventSource = 0;
113 	delete _audiocdManager;
114 	_audiocdManager = 0;
115 	delete _mixerManager;
116 	_mixerManager = 0;
117 
118 #ifdef ENABLE_EVENTRECORDER
119 	// HACK HACK HACK
120 	// This is nasty.
121 	delete g_eventRec.getTimerManager();
122 #else
123 	delete _timerManager;
124 #endif
125 
126 	_timerManager = 0;
127 	delete _mutexManager;
128 	_mutexManager = 0;
129 
130 	delete _logger;
131 	_logger = 0;
132 
133 #ifdef USE_SDL_NET
134 	if (_initedSDLnet) SDLNet_Quit();
135 #endif
136 
137 	SDL_Quit();
138 }
139 
init()140 void OSystem_SDL::init() {
141 	// Initialize SDL
142 	initSDL();
143 
144 #if !SDL_VERSION_ATLEAST(2, 0, 0)
145 	// Enable unicode support if possible
146 	SDL_EnableUNICODE(1);
147 #endif
148 
149 	// Disable OS cursor
150 	SDL_ShowCursor(SDL_DISABLE);
151 
152 	if (!_logger)
153 		_logger = new Backends::Log::Log(this);
154 
155 	if (_logger) {
156 		Common::WriteStream *logFile = createLogFile();
157 		if (logFile)
158 			_logger->open(logFile);
159 	}
160 
161 
162 	// Creates the early needed managers, if they don't exist yet
163 	// (we check for this to allow subclasses to provide their own).
164 	if (_mutexManager == 0)
165 		_mutexManager = new SdlMutexManager();
166 
167 	if (_window == 0)
168 		_window = new SdlWindow();
169 
170 #if defined(USE_TASKBAR)
171 	if (_taskbarManager == 0)
172 		_taskbarManager = new Common::TaskbarManager();
173 #endif
174 
175 }
176 
hasFeature(Feature f)177 bool OSystem_SDL::hasFeature(Feature f) {
178 #if SDL_VERSION_ATLEAST(2, 0, 0)
179 	if (f == kFeatureClipboardSupport) return true;
180 #endif
181 	if (f == kFeatureJoystickDeadzone || f == kFeatureKbdMouseSpeed) {
182 		bool joystickSupportEnabled = ConfMan.getInt("joystick_num") >= 0;
183 		return joystickSupportEnabled;
184 	}
185 	return ModularBackend::hasFeature(f);
186 }
187 
initBackend()188 void OSystem_SDL::initBackend() {
189 	// Check if backend has not been initialized
190 	assert(!_inited);
191 
192 #if SDL_VERSION_ATLEAST(2, 0, 0)
193 	const char *sdlDriverName = SDL_GetCurrentVideoDriver();
194 #else
195 	const int maxNameLen = 20;
196 	char sdlDriverName[maxNameLen];
197 	sdlDriverName[0] = '\0';
198 	SDL_VideoDriverName(sdlDriverName, maxNameLen);
199 #endif
200 	debug(1, "Using SDL Video Driver \"%s\"", sdlDriverName);
201 
202 	// Create the default event source, in case a custom backend
203 	// manager didn't provide one yet.
204 	if (_eventSource == 0)
205 		_eventSource = new SdlEventSource();
206 
207 	if (_eventManager == nullptr) {
208 		DefaultEventManager *eventManager = new DefaultEventManager(_eventSource);
209 #if SDL_VERSION_ATLEAST(2, 0, 0)
210 		// SDL 2 generates its own keyboard repeat events.
211 		eventManager->setGenerateKeyRepeatEvents(false);
212 #endif
213 		_eventManager = eventManager;
214 	}
215 
216 
217 #ifdef USE_OPENGL
218 #if SDL_VERSION_ATLEAST(2, 0, 0)
219 	SDL_DisplayMode displayMode;
220 	if (!SDL_GetDesktopDisplayMode(0, &displayMode)) {
221 		_desktopWidth  = displayMode.w;
222 		_desktopHeight = displayMode.h;
223 	}
224 #else
225 	// Query the desktop resolution. We simply hope nothing tried to change
226 	// the resolution so far.
227 	const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
228 	if (videoInfo && videoInfo->current_w > 0 && videoInfo->current_h > 0) {
229 		_desktopWidth  = videoInfo->current_w;
230 		_desktopHeight = videoInfo->current_h;
231 	}
232 #endif
233 #endif
234 
235 	if (_graphicsManager == 0) {
236 #ifdef USE_OPENGL
237 		// Setup a list with both SDL and OpenGL graphics modes. We only do
238 		// this whenever the subclass did not already set up an graphics
239 		// manager yet. This is because we don't know the type of the graphics
240 		// manager of the subclass, thus we cannot easily switch between the
241 		// OpenGL one and the set up one. It also is to be expected that the
242 		// subclass does not want any switching of graphics managers anyway.
243 		setupGraphicsModes();
244 
245 		if (ConfMan.hasKey("gfx_mode")) {
246 			// If the gfx_mode is from OpenGL, create the OpenGL graphics manager
247 			Common::String gfxMode(ConfMan.get("gfx_mode"));
248 			for (uint i = _firstGLMode; i < _graphicsModeIds.size(); ++i) {
249 				if (!scumm_stricmp(_graphicsModes[i].name, gfxMode.c_str())) {
250 					_graphicsManager = new OpenGLSdlGraphicsManager(_desktopWidth, _desktopHeight, _eventSource, _window);
251 					_graphicsMode = i;
252 					break;
253 				}
254 			}
255 		}
256 #endif
257 
258 		if (_graphicsManager == 0) {
259 			_graphicsManager = new SurfaceSdlGraphicsManager(_eventSource, _window);
260 		}
261 	}
262 
263 	if (_savefileManager == 0)
264 		_savefileManager = new DefaultSaveFileManager();
265 
266 	if (_mixerManager == 0) {
267 		_mixerManager = new SdlMixerManager();
268 		// Setup and start mixer
269 		_mixerManager->init();
270 	}
271 
272 #ifdef ENABLE_EVENTRECORDER
273 	g_eventRec.registerMixerManager(_mixerManager);
274 
275 	g_eventRec.registerTimerManager(new SdlTimerManager());
276 #else
277 	if (_timerManager == 0)
278 		_timerManager = new SdlTimerManager();
279 #endif
280 
281 	_audiocdManager = createAudioCDManager();
282 
283 	// Setup a custom program icon.
284 	_window->setupIcon();
285 
286 	_inited = true;
287 
288 	ModularBackend::initBackend();
289 
290 	// We have to initialize the graphics manager before the event manager
291 	// so the virtual keyboard can be initialized, but we have to add the
292 	// graphics manager as an event observer after initializing the event
293 	// manager.
294 	dynamic_cast<SdlGraphicsManager *>(_graphicsManager)->activateManager();
295 }
296 
engineInit()297 void OSystem_SDL::engineInit() {
298 #if SDL_VERSION_ATLEAST(2, 0, 0)
299 	dynamic_cast<SdlGraphicsManager *>(_graphicsManager)->unlockWindowSize();
300 #endif
301 #ifdef USE_TASKBAR
302 	// Add the started engine to the list of recent tasks
303 	_taskbarManager->addRecent(ConfMan.getActiveDomainName(), ConfMan.get("description"));
304 
305 	// Set the overlay icon the current running engine
306 	_taskbarManager->setOverlayIcon(ConfMan.getActiveDomainName(), ConfMan.get("description"));
307 #endif
308 }
309 
engineDone()310 void OSystem_SDL::engineDone() {
311 #if SDL_VERSION_ATLEAST(2, 0, 0)
312 	dynamic_cast<SdlGraphicsManager *>(_graphicsManager)->unlockWindowSize();
313 #endif
314 #ifdef USE_TASKBAR
315 	// Remove overlay icon
316 	_taskbarManager->setOverlayIcon("", "");
317 #endif
318 }
319 
initSDL()320 void OSystem_SDL::initSDL() {
321 	// Check if SDL has not been initialized
322 	if (!_initedSDL) {
323 		// We always initialize the video subsystem because we will need it to
324 		// be initialized before the graphics managers to retrieve the desktop
325 		// resolution, for example. WebOS also requires this initialization
326 		// or otherwise the application won't start.
327 		uint32 sdlFlags = SDL_INIT_VIDEO;
328 
329 		if (ConfMan.hasKey("disable_sdl_parachute"))
330 			sdlFlags |= SDL_INIT_NOPARACHUTE;
331 
332 		// Initialize SDL (SDL Subsystems are initiliazed in the corresponding sdl managers)
333 		if (SDL_Init(sdlFlags) == -1)
334 			error("Could not initialize SDL: %s", SDL_GetError());
335 
336 		_initedSDL = true;
337 	}
338 
339 #ifdef USE_SDL_NET
340 	// Check if SDL_net has not been initialized
341 	if (!_initedSDLnet) {
342 		// Initialize SDL_net
343 		if (SDLNet_Init() == -1)
344 			error("Could not initialize SDL_net: %s", SDLNet_GetError());
345 
346 		_initedSDLnet = true;
347 	}
348 #endif
349 }
350 
addSysArchivesToSearchSet(Common::SearchSet & s,int priority)351 void OSystem_SDL::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
352 
353 #ifdef DATA_PATH
354 	// Add the global DATA_PATH to the directory search list
355 	// FIXME: We use depth = 4 for now, to match the old code. May want to change that
356 	Common::FSNode dataNode(DATA_PATH);
357 	if (dataNode.exists() && dataNode.isDirectory()) {
358 		s.add(DATA_PATH, new Common::FSDirectory(dataNode, 4), priority);
359 	}
360 #endif
361 
362 }
363 
setWindowCaption(const char * caption)364 void OSystem_SDL::setWindowCaption(const char *caption) {
365 	Common::String cap;
366 	byte c;
367 
368 	// The string caption is supposed to be in LATIN-1 encoding.
369 	// SDL expects UTF-8. So we perform the conversion here.
370 	while ((c = *(const byte *)caption++)) {
371 		if (c < 0x80)
372 			cap += c;
373 		else {
374 			cap += 0xC0 | (c >> 6);
375 			cap += 0x80 | (c & 0x3F);
376 		}
377 	}
378 
379 	_window->setWindowCaption(cap);
380 }
381 
quit()382 void OSystem_SDL::quit() {
383 	destroy();
384 	exit(0);
385 }
386 
fatalError()387 void OSystem_SDL::fatalError() {
388 	destroy();
389 	exit(1);
390 }
391 
392 
logMessage(LogMessageType::Type type,const char * message)393 void OSystem_SDL::logMessage(LogMessageType::Type type, const char *message) {
394 	// First log to stdout/stderr
395 	FILE *output = 0;
396 
397 	if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
398 		output = stdout;
399 	else
400 		output = stderr;
401 
402 	fputs(message, output);
403 	fflush(output);
404 
405 	// Then log into file (via the logger)
406 	if (_logger)
407 		_logger->print(message);
408 }
409 
createLogFile()410 Common::WriteStream *OSystem_SDL::createLogFile() {
411 	// Start out by resetting _logFilePath, so that in case
412 	// of a failure, we know that no log file is open.
413 	_logFilePath.clear();
414 
415 	Common::String logFile = getDefaultLogFileName();
416 	if (logFile.empty())
417 		return nullptr;
418 
419 	Common::FSNode file(logFile);
420 	Common::WriteStream *stream = file.createWriteStream();
421 	if (stream)
422 		_logFilePath = logFile;
423 	return stream;
424 }
425 
getSystemLanguage() const426 Common::String OSystem_SDL::getSystemLanguage() const {
427 #if defined(USE_DETECTLANG) && !defined(WIN32)
428 	// Activating current locale settings
429 	const Common::String locale = setlocale(LC_ALL, "");
430 
431 	// Restore default C locale to prevent issues with
432 	// portability of sscanf(), atof(), etc.
433 	// See bug #3615148
434 	setlocale(LC_ALL, "C");
435 
436 	// Detect the language from the locale
437 	if (locale.empty()) {
438 		return ModularBackend::getSystemLanguage();
439 	} else {
440 		int length = 0;
441 
442 		// Strip out additional information, like
443 		// ".UTF-8" or the like. We do this, since
444 		// our translation languages are usually
445 		// specified without any charset information.
446 		for (int size = locale.size(); length < size; ++length) {
447 			// TODO: Check whether "@" should really be checked
448 			// here.
449 			if (locale[length] == '.' || locale[length] == ' ' || locale[length] == '@')
450 				break;
451 		}
452 
453 		return Common::String(locale.c_str(), length);
454 	}
455 #else // USE_DETECTLANG
456 	return ModularBackend::getSystemLanguage();
457 #endif // USE_DETECTLANG
458 }
459 
460 #if SDL_VERSION_ATLEAST(2, 0, 0)
hasTextInClipboard()461 bool OSystem_SDL::hasTextInClipboard() {
462 	return SDL_HasClipboardText() == SDL_TRUE;
463 }
464 
getTextFromClipboard()465 Common::String OSystem_SDL::getTextFromClipboard() {
466 	if (!hasTextInClipboard()) return "";
467 
468 	char *text = SDL_GetClipboardText();
469 	// The string returned by SDL is in UTF-8. Convert to the
470 	// current TranslationManager encoding or ISO-8859-1.
471 #ifdef USE_TRANSLATION
472 	char *conv_text = SDL_iconv_string(TransMan.getCurrentCharset().c_str(), "UTF-8", text, SDL_strlen(text) + 1);
473 #else
474 	char *conv_text = SDL_iconv_string("ISO-8859-1", "UTF-8", text, SDL_strlen(text) + 1);
475 #endif
476 	if (conv_text) {
477 		SDL_free(text);
478 		text = conv_text;
479 	}
480 	Common::String strText = text;
481 	SDL_free(text);
482 
483 	return strText;
484 }
485 
setTextInClipboard(const Common::String & text)486 bool OSystem_SDL::setTextInClipboard(const Common::String &text) {
487 	// The encoding we need to use is UTF-8. Assume we currently have the
488 	// current TranslationManager encoding or ISO-8859-1.
489 #ifdef USE_TRANSLATION
490 	char *utf8_text = SDL_iconv_string("UTF-8", TransMan.getCurrentCharset().c_str(), text.c_str(), text.size() + 1);
491 #else
492 	char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", text.c_str(), text.size() + 1);
493 #endif
494 	if (utf8_text) {
495 		int status = SDL_SetClipboardText(utf8_text);
496 		SDL_free(utf8_text);
497 		return status == 0;
498 	}
499 	return SDL_SetClipboardText(text.c_str()) == 0;
500 }
501 #endif
502 
getMillis(bool skipRecord)503 uint32 OSystem_SDL::getMillis(bool skipRecord) {
504 	uint32 millis = SDL_GetTicks();
505 
506 #ifdef ENABLE_EVENTRECORDER
507 	g_eventRec.processMillis(millis, skipRecord);
508 #endif
509 
510 	return millis;
511 }
512 
delayMillis(uint msecs)513 void OSystem_SDL::delayMillis(uint msecs) {
514 #ifdef ENABLE_EVENTRECORDER
515 	if (!g_eventRec.processDelayMillis())
516 #endif
517 		SDL_Delay(msecs);
518 }
519 
getTimeAndDate(TimeDate & td) const520 void OSystem_SDL::getTimeAndDate(TimeDate &td) const {
521 	time_t curTime = time(0);
522 	struct tm t = *localtime(&curTime);
523 	td.tm_sec = t.tm_sec;
524 	td.tm_min = t.tm_min;
525 	td.tm_hour = t.tm_hour;
526 	td.tm_mday = t.tm_mday;
527 	td.tm_mon = t.tm_mon;
528 	td.tm_year = t.tm_year;
529 	td.tm_wday = t.tm_wday;
530 }
531 
getMixer()532 Audio::Mixer *OSystem_SDL::getMixer() {
533 	assert(_mixerManager);
534 	return getMixerManager()->getMixer();
535 }
536 
getMixerManager()537 SdlMixerManager *OSystem_SDL::getMixerManager() {
538 	assert(_mixerManager);
539 
540 #ifdef ENABLE_EVENTRECORDER
541 	return g_eventRec.getMixerManager();
542 #else
543 	return _mixerManager;
544 #endif
545 }
546 
getTimerManager()547 Common::TimerManager *OSystem_SDL::getTimerManager() {
548 #ifdef ENABLE_EVENTRECORDER
549 	return g_eventRec.getTimerManager();
550 #else
551 	return _timerManager;
552 #endif
553 }
554 
createAudioCDManager()555 AudioCDManager *OSystem_SDL::createAudioCDManager() {
556 	// Audio CD support was removed with SDL 2.0
557 #if SDL_VERSION_ATLEAST(2, 0, 0)
558 	return new DefaultAudioCDManager();
559 #else
560 	return new SdlAudioCDManager();
561 #endif
562 }
563 
getSavefileManager()564 Common::SaveFileManager *OSystem_SDL::getSavefileManager() {
565 #ifdef ENABLE_EVENTRECORDER
566     return g_eventRec.getSaveManager(_savefileManager);
567 #else
568     return _savefileManager;
569 #endif
570 }
571 
572 //Not specified in base class
getScreenshotsPath()573 Common::String OSystem_SDL::getScreenshotsPath() {
574 	Common::String path = ConfMan.get("screenshotpath");
575 	if (!path.empty() && !path.hasSuffix("/"))
576 		path += "/";
577 	return path;
578 }
579 
580 #ifdef USE_OPENGL
581 
getSupportedGraphicsModes() const582 const OSystem::GraphicsMode *OSystem_SDL::getSupportedGraphicsModes() const {
583 	if (_graphicsModes.empty()) {
584 		return _graphicsManager->getSupportedGraphicsModes();
585 	} else {
586 		return _graphicsModes.begin();
587 	}
588 }
589 
getDefaultGraphicsMode() const590 int OSystem_SDL::getDefaultGraphicsMode() const {
591 	if (_graphicsModes.empty()) {
592 		return _graphicsManager->getDefaultGraphicsMode();
593 	} else {
594 		// Return the default graphics mode from the current graphics manager
595 		if (_graphicsMode < _firstGLMode)
596 			return _defaultSDLMode;
597 		else
598 			return _defaultGLMode;
599 	}
600 }
601 
setGraphicsMode(int mode)602 bool OSystem_SDL::setGraphicsMode(int mode) {
603 	if (_graphicsModes.empty()) {
604 		return _graphicsManager->setGraphicsMode(mode);
605 	}
606 
607 	// Check whether a invalid mode is requested.
608 	if (mode < 0 || (uint)mode >= _graphicsModeIds.size()) {
609 		return false;
610 	}
611 
612 	// Very hacky way to set up the old graphics manager state, in case we
613 	// switch from SDL->OpenGL or OpenGL->SDL.
614 	//
615 	// This is a probably temporary workaround to fix bugs like #3368143
616 	// "SDL/OpenGL: Crash when switching renderer backend".
617 	SdlGraphicsManager *sdlGraphicsManager = dynamic_cast<SdlGraphicsManager *>(_graphicsManager);
618 	SdlGraphicsManager::State state = sdlGraphicsManager->getState();
619 
620 	bool switchedManager = false;
621 
622 	// If the new mode and the current mode are not from the same graphics
623 	// manager, delete and create the new mode graphics manager
624 	if (_graphicsMode >= _firstGLMode && mode < _firstGLMode) {
625 		debug(1, "switching to plain SDL graphics");
626 		sdlGraphicsManager->deactivateManager();
627 		delete _graphicsManager;
628 		_graphicsManager = sdlGraphicsManager = new SurfaceSdlGraphicsManager(_eventSource, _window);
629 
630 		switchedManager = true;
631 	} else if (_graphicsMode < _firstGLMode && mode >= _firstGLMode) {
632 		debug(1, "switching to OpenGL graphics");
633 		sdlGraphicsManager->deactivateManager();
634 		delete _graphicsManager;
635 		_graphicsManager = sdlGraphicsManager = new OpenGLSdlGraphicsManager(_desktopWidth, _desktopHeight, _eventSource, _window);
636 
637 		switchedManager = true;
638 	}
639 
640 	_graphicsMode = mode;
641 
642 	if (switchedManager) {
643 		sdlGraphicsManager->activateManager();
644 
645 		// This failing will probably have bad consequences...
646 		if (!sdlGraphicsManager->setState(state)) {
647 			return false;
648 		}
649 
650 		// Next setup the cursor again
651 		CursorMan.pushCursor(0, 0, 0, 0, 0, 0);
652 		CursorMan.popCursor();
653 
654 		// Next setup cursor palette if needed
655 		if (_graphicsManager->getFeatureState(kFeatureCursorPalette)) {
656 			CursorMan.pushCursorPalette(0, 0, 0);
657 			CursorMan.popCursorPalette();
658 		}
659 
660 		_graphicsManager->beginGFXTransaction();
661 		// Oh my god if this failed the client code might just explode.
662 		return _graphicsManager->setGraphicsMode(_graphicsModeIds[mode]);
663 	} else {
664 		return _graphicsManager->setGraphicsMode(_graphicsModeIds[mode]);
665 	}
666 }
667 
getGraphicsMode() const668 int OSystem_SDL::getGraphicsMode() const {
669 	if (_graphicsModes.empty()) {
670 		return _graphicsManager->getGraphicsMode();
671 	} else {
672 		return _graphicsMode;
673 	}
674 }
675 
setupGraphicsModes()676 void OSystem_SDL::setupGraphicsModes() {
677 	_graphicsModes.clear();
678 	_graphicsModeIds.clear();
679 	_defaultSDLMode = _defaultGLMode = -1;
680 
681 	// Count the number of graphics modes
682 	const OSystem::GraphicsMode *srcMode;
683 	int defaultMode;
684 
685 	GraphicsManager *manager = new SurfaceSdlGraphicsManager(_eventSource, _window);
686 	srcMode = manager->getSupportedGraphicsModes();
687 	defaultMode = manager->getDefaultGraphicsMode();
688 	while (srcMode->name) {
689 		if (defaultMode == srcMode->id) {
690 			_defaultSDLMode = _graphicsModes.size();
691 		}
692 		_graphicsModes.push_back(*srcMode);
693 		srcMode++;
694 	}
695 	delete manager;
696 	assert(_defaultSDLMode != -1);
697 
698 	_firstGLMode = _graphicsModes.size();
699 	manager = new OpenGLSdlGraphicsManager(_desktopWidth, _desktopHeight, _eventSource, _window);
700 	srcMode = manager->getSupportedGraphicsModes();
701 	defaultMode = manager->getDefaultGraphicsMode();
702 	while (srcMode->name) {
703 		if (defaultMode == srcMode->id) {
704 			_defaultGLMode = _graphicsModes.size();
705 		}
706 		_graphicsModes.push_back(*srcMode);
707 		srcMode++;
708 	}
709 	delete manager;
710 	manager = nullptr;
711 	assert(_defaultGLMode != -1);
712 
713 	// Set a null mode at the end
714 	GraphicsMode nullMode;
715 	memset(&nullMode, 0, sizeof(nullMode));
716 	_graphicsModes.push_back(nullMode);
717 
718 	// Set new internal ids for all modes
719 	int i = 0;
720 	OSystem::GraphicsMode *mode = _graphicsModes.begin();
721 	while (mode->name) {
722 		_graphicsModeIds.push_back(mode->id);
723 		mode->id = i++;
724 		mode++;
725 	}
726 }
727 #endif
728 
729 #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_SetColors(SDL_Surface * surface,SDL_Color * colors,int firstcolor,int ncolors)730 int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors) {
731 	if (surface->format->palette) {
732 		return !SDL_SetPaletteColors(surface->format->palette, colors, firstcolor, ncolors) ? 1 : 0;
733 	} else {
734 		return 0;
735 	}
736 }
737 
SDL_SetAlpha(SDL_Surface * surface,Uint32 flag,Uint8 alpha)738 int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha) {
739 	if (SDL_SetSurfaceAlphaMod(surface, alpha)) {
740 		return -1;
741 	}
742 
743 	if (alpha == 255 || !flag) {
744 		if (SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE)) {
745 			return -1;
746 		}
747 	} else {
748 		if (SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND)) {
749 			return -1;
750 		}
751 	}
752 
753 	return 0;
754 }
755 
756 #undef SDL_SetColorKey
SDL_SetColorKey_replacement(SDL_Surface * surface,Uint32 flag,Uint32 key)757 int SDL_SetColorKey_replacement(SDL_Surface *surface, Uint32 flag, Uint32 key) {
758 	return SDL_SetColorKey(surface, SDL_TRUE, key) ? -1 : 0;
759 }
760 #endif
761 
convertEncoding(const char * to,const char * from,const char * string,size_t length)762 char *OSystem_SDL::convertEncoding(const char *to, const char *from, const char *string, size_t length) {
763 #if SDL_VERSION_ATLEAST(1, 2, 10)
764 	int zeroBytes = 1;
765 	if (Common::String(from).hasPrefixIgnoreCase("utf-16"))
766 		zeroBytes = 2;
767 	else if (Common::String(from).hasPrefixIgnoreCase("utf-32"))
768 		zeroBytes = 4;
769 
770 	char *result;
771 	// SDL_iconv_string() takes char * instead of const char * as it's third parameter
772 	// with some older versions of SDL.
773 #if SDL_VERSION_ATLEAST(2, 0, 0)
774 	result = SDL_iconv_string(to, from, string, length + zeroBytes);
775 #else
776 	char *stringCopy = (char *) calloc(sizeof(char), length + zeroBytes);
777 	memcpy(stringCopy, string, length);
778 	result = SDL_iconv_string(to, from, stringCopy, length + zeroBytes);
779 	free(stringCopy);
780 #endif // SDL_VERSION_ATLEAST(2, 0, 0)
781 	if (result == nullptr)
782 		return nullptr;
783 
784 	// We need to copy the result, so that we can use SDL_free()
785 	// on the string returned by SDL_iconv_string() and free()
786 	// can then be used on the copyed and returned string.
787 	// Sometimes free() and SDL_free() aren't compatible and
788 	// using free() instead of SDL_free() can cause crashes.
789 	size_t newLength = Common::Encoding::stringLength(result, to);
790 	zeroBytes = 1;
791 	if (Common::String(to).hasPrefixIgnoreCase("utf-16"))
792 		zeroBytes = 2;
793 	else if (Common::String(to).hasPrefixIgnoreCase("utf-32"))
794 		zeroBytes = 4;
795 	char *finalResult = (char *) malloc(newLength + zeroBytes);
796 	if (!finalResult) {
797 		warning("Could not allocate memory for encoding conversion");
798 		SDL_free(result);
799 		return nullptr;
800 	}
801 	memcpy(finalResult, result, newLength + zeroBytes);
802 	SDL_free(result);
803 	return finalResult;
804 #else
805 	return ModularBackend::convertEncoding(to, from, string, length);
806 #endif // SDL_VERSION_ATLEAST(1, 2, 10)
807 }
808 
809