1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name sdl.cpp - SDL video support. */
12 //
13 //      (c) Copyright 1999-2011 by Lutz Sammer, Jimmy Salmon, Nehal Mistry and
14 //                                 Pali Rohár
15 //
16 //      This program is free software; you can redistribute it and/or modify
17 //      it under the terms of the GNU General Public License as published by
18 //      the Free Software Foundation; only version 2 of the License.
19 //
20 //      This program is distributed in the hope that it will be useful,
21 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
22 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 //      GNU General Public License for more details.
24 //
25 //      You should have received a copy of the GNU General Public License
26 //      along with this program; if not, write to the Free Software
27 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 //      02111-1307, USA.
29 //
30 
31 //@{
32 
33 /*----------------------------------------------------------------------------
34 -- Includes
35 ----------------------------------------------------------------------------*/
36 
37 #include "stratagus.h"
38 
39 #ifdef DEBUG
40 #include <signal.h>
41 #endif
42 
43 #include <map>
44 #include <string>
45 #include <vector>
46 
47 
48 #include <limits.h>
49 #include <math.h>
50 
51 #ifndef USE_WIN32
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #endif
55 
56 #include "SDL.h"
57 #include "SDL_syswm.h"
58 
59 #ifdef USE_GLES_EGL
60 #include "EGL/egl.h"
61 #endif
62 
63 #ifdef USE_GLES
64 #include "GLES/gl.h"
65 #endif
66 
67 #ifdef USE_OPENGL
68 #include "SDL_opengl.h"
69 #include "shaders.h"
70 #endif
71 
72 #ifdef USE_BEOS
73 #include <sys/socket.h>
74 #endif
75 
76 #ifdef USE_WIN32
77 #include <shellapi.h>
78 #endif
79 
80 #include "editor.h"
81 #include "font.h"
82 #include "game.h"
83 //Wyrmgus start
84 #include "grand_strategy.h"
85 //Wyrmgus end
86 #include "map/minimap.h"
87 #include "network.h"
88 #include "parameters.h"
89 #include "sound.h"
90 #include "sound_server.h"
91 #include "translate.h"
92 #include "ui/interface.h"
93 #include "ui/ui.h"
94 #include "unit/unit.h"
95 #include "version.h"
96 #include "video.h"
97 #include "widgets.h"
98 
99 /*----------------------------------------------------------------------------
100 --  Declarations
101 ----------------------------------------------------------------------------*/
102 
103 /*----------------------------------------------------------------------------
104 --  Variables
105 ----------------------------------------------------------------------------*/
106 
107 #ifdef USE_GLES_EGL
108 static EGLDisplay eglDisplay;
109 static EGLSurface eglSurface;
110 #endif
111 
112 SDL_Surface *TheScreen; /// Internal screen
113 
114 static SDL_Rect Rects[100];
115 static int NumRects;
116 
117 #if defined(USE_OPENGL) || defined(USE_GLES)
118 GLint GLMaxTextureSize = 256;   /// Max texture size supported on the video card
119 GLint GLMaxTextureSizeOverride;     /// User-specified limit for ::GLMaxTextureSize
120 bool GLTextureCompressionSupported; /// Is OpenGL texture compression supported
121 bool UseGLTextureCompression;       /// Use OpenGL texture compression
122 #endif
123 
124 static std::map<int, std::string> Key2Str;
125 static std::map<std::string, int> Str2Key;
126 
127 double FrameTicks;     /// Frame length in ms
128 
129 const EventCallback *Callbacks = nullptr;
130 
131 static bool RegenerateScreen = false;
132 bool IsSDLWindowVisible = true;
133 
134 /*----------------------------------------------------------------------------
135 --  Functions
136 ----------------------------------------------------------------------------*/
137 
138 // ARB_texture_compression
139 #ifdef USE_OPENGL
140 PFNGLCOMPRESSEDTEXIMAGE3DARBPROC    glCompressedTexImage3DARB;
141 PFNGLCOMPRESSEDTEXIMAGE2DARBPROC    glCompressedTexImage2DARB;
142 PFNGLCOMPRESSEDTEXIMAGE1DARBPROC    glCompressedTexImage1DARB;
143 PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glCompressedTexSubImage3DARB;
144 PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glCompressedTexSubImage2DARB;
145 PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glCompressedTexSubImage1DARB;
146 PFNGLGETCOMPRESSEDTEXIMAGEARBPROC   glGetCompressedTexImageARB;
147 #endif
148 
149 /*----------------------------------------------------------------------------
150 --  Sync
151 ----------------------------------------------------------------------------*/
152 
153 /**
154 **  Initialise video sync.
155 **  Calculate the length of video frame and any simulation skips.
156 **
157 **  @see VideoSyncSpeed @see SkipFrames @see FrameTicks
158 */
SetVideoSync()159 void SetVideoSync()
160 {
161 	double ms;
162 
163 	if (VideoSyncSpeed) {
164 		ms = (1000.0 * 1000.0 / CYCLES_PER_SECOND) / VideoSyncSpeed;
165 	} else {
166 		ms = (double)INT_MAX;
167 	}
168 	SkipFrames = ms / 400;
169 	while (SkipFrames && ms / SkipFrames < 200) {
170 		--SkipFrames;
171 	}
172 	ms /= SkipFrames + 1;
173 
174 	FrameTicks = ms / 10;
175 	DebugPrint("frames %d - %5.2fms\n" _C_ SkipFrames _C_ ms / 10);
176 }
177 
178 /*----------------------------------------------------------------------------
179 --  Video
180 ----------------------------------------------------------------------------*/
181 
182 #ifdef USE_OPENGL
183 /**
184 **  Check if an extension is supported
185 */
IsExtensionSupported(const char * extension)186 static bool IsExtensionSupported(const char *extension)
187 {
188 	const GLubyte *extensions = nullptr;
189 	const GLubyte *start;
190 	GLubyte *ptr, *terminator;
191 	int len;
192 
193 	// Extension names should not have spaces.
194 	ptr = (GLubyte *)strchr(extension, ' ');
195 	if (ptr || *extension == '\0') {
196 		return false;
197 	}
198 
199 	extensions = glGetString(GL_EXTENSIONS);
200 	len = strlen(extension);
201 	start = extensions;
202 	while (true) {
203 		ptr = (GLubyte *)strstr((const char *)start, extension);
204 		if (!ptr) {
205 			break;
206 		}
207 
208 		terminator = ptr + len;
209 		if (ptr == start || *(ptr - 1) == ' ') {
210 			if (*terminator == ' ' || *terminator == '\0') {
211 				return true;
212 			}
213 		}
214 		start = terminator;
215 	}
216 	return false;
217 }
218 #endif
219 
220 #if defined(USE_OPENGL) || defined(USE_GLES)
221 
222 /**
223 **  Initialize OpenGL extensions
224 */
InitOpenGLExtensions()225 static void InitOpenGLExtensions()
226 {
227 	// ARB_texture_compression
228 #ifdef USE_OPENGL
229 	if (IsExtensionSupported("GL_ARB_texture_compression")) {
230 		glCompressedTexImage3DARB =
231 			(PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)(uintptr_t)SDL_GL_GetProcAddress("glCompressedTexImage3DARB");
232 		glCompressedTexImage2DARB =
233 			(PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)(uintptr_t)SDL_GL_GetProcAddress("glCompressedTexImage2DARB");
234 		glCompressedTexImage1DARB =
235 			(PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)(uintptr_t)SDL_GL_GetProcAddress("glCompressedTexImage1DARB");
236 		glCompressedTexSubImage3DARB =
237 			(PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)(uintptr_t)SDL_GL_GetProcAddress("glCompressedTexSubImage3DARB");
238 		glCompressedTexSubImage2DARB =
239 			(PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)(uintptr_t)SDL_GL_GetProcAddress("glCompressedTexSubImage2DARB");
240 		glCompressedTexSubImage1DARB =
241 			(PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)(uintptr_t)SDL_GL_GetProcAddress("glCompressedTexSubImage1DARB");
242 		glGetCompressedTexImageARB =
243 			(PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)(uintptr_t)SDL_GL_GetProcAddress("glGetCompressedTexImageARB");
244 
245 		if (glCompressedTexImage3DARB && glCompressedTexImage2DARB &&
246 			glCompressedTexImage1DARB && glCompressedTexSubImage3DARB &&
247 			glCompressedTexSubImage2DARB && glCompressedTexSubImage1DARB &&
248 			glGetCompressedTexImageARB) {
249 			GLTextureCompressionSupported = true;
250 		} else {
251 			GLTextureCompressionSupported = false;
252 		}
253 
254 		GLShaderPipelineSupported = GLShaderPipelineSupported && LoadShaderExtensions();
255 	} else {
256 		GLTextureCompressionSupported = false;
257 		GLShaderPipelineSupported = false;
258 	}
259 #else
260 	GLTextureCompressionSupported = false;
261 #endif
262 }
263 
264 /**
265 **  Initialize OpenGL
266 */
InitOpenGL()267 static void InitOpenGL()
268 {
269 
270 	InitOpenGLExtensions();
271 
272 	glViewport(0, 0, (GLsizei)Video.ViewportWidth, (GLsizei)Video.ViewportHeight);
273 
274 #ifdef USE_OPENGL
275 	glMatrixMode(GL_PROJECTION);
276 	glLoadIdentity();
277 #endif
278 
279 #ifdef USE_GLES
280 	glOrthof(0.0f, (GLfloat)Video.Width, (GLfloat)Video.Height, 0.0f, -1.0f, 1.0f);
281 #endif
282 
283 #ifdef USE_OPENGL
284 	if (!GLShaderPipelineSupported) {
285 		glOrtho(0, Video.Width, Video.Height, 0, -1, 1);
286 	} else {
287 		glOrtho(0, Video.ViewportWidth, Video.ViewportHeight, 0, -1, 1);
288 	}
289 #endif
290 
291 	glMatrixMode(GL_MODELVIEW);
292 	glLoadIdentity();
293 
294 #ifdef USE_OPENGL
295 	glTranslatef(0.375, 0.375, 0.);
296 #endif
297 
298 	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
299 
300 #ifdef USE_GLES
301 	glClearDepthf(1.0f);
302 #endif
303 
304 #ifdef USE_OPENGL
305 	glClearDepth(1.0f);
306 
307 	if (GLShaderPipelineSupported) {
308 		SetupFramebuffer();
309 	}
310 #endif
311 
312 	glShadeModel(GL_FLAT);
313 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
314 	glEnable(GL_BLEND);
315 	glEnable(GL_TEXTURE_2D);
316 	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
317 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
318 	glEnable(GL_LINE_SMOOTH);
319 	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
320 
321 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &GLMaxTextureSize);
322 	if (GLMaxTextureSize == 0) {
323 		// FIXME: try to use GL_PROXY_TEXTURE_2D to get a valid size
324 #if 0
325 		glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, size, size, 0,
326 					 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
327 		glGetTexLevelParameterfv(GL_PROXY_TEXTURE_2D, 0,
328 								 GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
329 #endif
330 		fprintf(stderr, "GL_MAX_TEXTURE_SIZE is 0, using 256 by default\n");
331 		GLMaxTextureSize = 256;
332 	}
333 	if (GLMaxTextureSize > GLMaxTextureSizeOverride
334 		&& GLMaxTextureSizeOverride > 0) {
335 		GLMaxTextureSize = GLMaxTextureSizeOverride;
336 	}
337 }
338 
ReloadOpenGL()339 void ReloadOpenGL()
340 {
341 	InitOpenGL();
342 	ReloadGraphics();
343 	ReloadFonts();
344 	UI.Minimap.Reload();
345 }
346 
347 #endif
348 
349 #if defined(DEBUG) && !defined(USE_WIN32)
CleanExit(int)350 static void CleanExit(int)
351 {
352 	// Clean SDL
353 	SDL_Quit();
354 	// Reestablish normal behaviour for next abort call
355 	signal(SIGABRT, SIG_DFL);
356 	// Generates a core dump
357 	abort();
358 }
359 #endif
360 
361 /**
362 **  Initialize SDLKey to string map
363 */
InitKey2Str()364 static void InitKey2Str()
365 {
366 	Str2Key[_("esc")] = SDLK_ESCAPE;
367 
368 	if (!Key2Str.empty()) {
369 		return;
370 	}
371 
372 	int i;
373 	char str[20];
374 
375 	Key2Str[SDLK_BACKSPACE] = "backspace";
376 	Key2Str[SDLK_TAB] = "tab";
377 	Key2Str[SDLK_CLEAR] = "clear";
378 	Key2Str[SDLK_RETURN] = "return";
379 	Key2Str[SDLK_PAUSE] = "pause";
380 	Key2Str[SDLK_ESCAPE] = "escape";
381 	Key2Str[SDLK_SPACE] = " ";
382 	Key2Str[SDLK_EXCLAIM] = "!";
383 	Key2Str[SDLK_QUOTEDBL] = "\"";
384 	Key2Str[SDLK_HASH] = "#";
385 	Key2Str[SDLK_DOLLAR] = "$";
386 	Key2Str[SDLK_AMPERSAND] = "&";
387 	Key2Str[SDLK_QUOTE] = "'";
388 	Key2Str[SDLK_LEFTPAREN] = "(";
389 	Key2Str[SDLK_RIGHTPAREN] = ")";
390 	Key2Str[SDLK_ASTERISK] = "*";
391 	Key2Str[SDLK_PLUS] = "+";
392 	Key2Str[SDLK_COMMA] = ",";
393 	Key2Str[SDLK_MINUS] = "-";
394 	Key2Str[SDLK_PERIOD] = ".";
395 	Key2Str[SDLK_SLASH] = "/";
396 
397 	str[1] = '\0';
398 	for (i = SDLK_0; i <= SDLK_9; ++i) {
399 		str[0] = i;
400 		Key2Str[i] = str;
401 	}
402 
403 	Key2Str[SDLK_COLON] = ":";
404 	Key2Str[SDLK_SEMICOLON] = ";";
405 	Key2Str[SDLK_LESS] = "<";
406 	Key2Str[SDLK_EQUALS] = "=";
407 	Key2Str[SDLK_GREATER] = ">";
408 	Key2Str[SDLK_QUESTION] = "?";
409 	Key2Str[SDLK_AT] = "@";
410 	Key2Str[SDLK_LEFTBRACKET] = "[";
411 	Key2Str[SDLK_BACKSLASH] = "\\";
412 	Key2Str[SDLK_RIGHTBRACKET] = "]";
413 	Key2Str[SDLK_BACKQUOTE] = "`";
414 
415 	str[1] = '\0';
416 	for (i = SDLK_a; i <= SDLK_z; ++i) {
417 		str[0] = i;
418 		Key2Str[i] = str;
419 	}
420 
421 	Key2Str[SDLK_DELETE] = "delete";
422 
423 	for (i = SDLK_KP0; i <= SDLK_KP9; ++i) {
424 		snprintf(str, sizeof(str), "kp_%d", i - SDLK_KP0);
425 		Key2Str[i] = str;
426 	}
427 
428 	Key2Str[SDLK_KP_PERIOD] = "kp_period";
429 	Key2Str[SDLK_KP_DIVIDE] = "kp_divide";
430 	Key2Str[SDLK_KP_MULTIPLY] = "kp_multiply";
431 	Key2Str[SDLK_KP_MINUS] = "kp_minus";
432 	Key2Str[SDLK_KP_PLUS] = "kp_plus";
433 	Key2Str[SDLK_KP_ENTER] = "kp_enter";
434 	Key2Str[SDLK_KP_EQUALS] = "kp_equals";
435 	Key2Str[SDLK_UP] = "up";
436 	Key2Str[SDLK_DOWN] = "down";
437 	Key2Str[SDLK_RIGHT] = "right";
438 	Key2Str[SDLK_LEFT] = "left";
439 	Key2Str[SDLK_INSERT] = "insert";
440 	Key2Str[SDLK_HOME] = "home";
441 	Key2Str[SDLK_END] = "end";
442 	Key2Str[SDLK_PAGEUP] = "pageup";
443 	Key2Str[SDLK_PAGEDOWN] = "pagedown";
444 
445 	for (i = SDLK_F1; i <= SDLK_F15; ++i) {
446 		snprintf(str, sizeof(str), "f%d", i - SDLK_F1 + 1);
447 		Key2Str[i] = str;
448 		snprintf(str, sizeof(str), "F%d", i - SDLK_F1 + 1);
449 		Str2Key[str] = i;
450 	}
451 
452 	Key2Str[SDLK_HELP] = "help";
453 	Key2Str[SDLK_PRINT] = "print";
454 	Key2Str[SDLK_SYSREQ] = "sysreq";
455 	Key2Str[SDLK_BREAK] = "break";
456 	Key2Str[SDLK_MENU] = "menu";
457 	Key2Str[SDLK_POWER] = "power";
458 	Key2Str[SDLK_EURO] = "euro";
459 	Key2Str[SDLK_UNDO] = "undo";
460 }
461 
462 /**
463 **  Initialize the video part for SDL.
464 */
InitVideoSdl()465 void InitVideoSdl()
466 {
467 	Uint32 flags = 0;
468 
469 	if (SDL_WasInit(SDL_INIT_VIDEO) == 0) {
470 //Wyrmgus start
471 //#ifndef USE_WIN32
472 //Wyrmgus end
473 		// Fix tablet input in full-screen mode
474 		#ifndef __MORPHOS__
475 		SDL_putenv(strdup("SDL_MOUSE_RELATIVE=0"));
476 		#endif
477 //Wyrmgus start
478 //#endif
479 //Wyrmgus end
480 		int res = SDL_Init(
481 #ifdef DEBUG
482 					  SDL_INIT_NOPARACHUTE |
483 #endif
484 					  SDL_INIT_AUDIO | SDL_INIT_VIDEO |
485 					  SDL_INIT_TIMER);
486 		if (res < 0) {
487 			fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
488 			exit(1);
489 		}
490 
491 		// Clean up on exit
492 		atexit(SDL_Quit);
493 
494 		// If debug is enabled, Stratagus disable SDL Parachute.
495 		// So we need gracefully handle segfaults and aborts.
496 #if defined(DEBUG) && !defined(USE_WIN32)
497 		signal(SIGSEGV, CleanExit);
498 		signal(SIGABRT, CleanExit);
499 #endif
500 		// Set WindowManager Title
501 		if (!FullGameName.empty()) {
502 			SDL_WM_SetCaption(FullGameName.c_str(), FullGameName.c_str());
503 		} else if (!Parameters::Instance.applicationName.empty()) {
504 			SDL_WM_SetCaption(Parameters::Instance.applicationName.c_str(), Parameters::Instance.applicationName.c_str());
505 		} else {
506 			SDL_WM_SetCaption(NAME, NAME);
507 		}
508 
509 #if ! defined(USE_WIN32)
510 
511 #if defined(USE_OPENGL) || defined(USE_GLES)
512 		// Make sure, that we not create OpenGL textures (and do not call OpenGL functions), when creating icon surface
513 		bool UseOpenGL_orig = UseOpenGL;
514 		UseOpenGL = false;
515 #endif
516 
517 #ifndef __MORPHOS__
518 		SDL_Surface *icon = nullptr;
519 		CGraphic *g = nullptr;
520 		struct stat st;
521 
522 		std::string FullGameNameL = FullGameName;
523 		for (size_t i = 0; i < FullGameNameL.size(); ++i) {
524 			FullGameNameL[i] = tolower(FullGameNameL[i]);
525 		}
526 
527 		std::string ApplicationName = Parameters::Instance.applicationName;
528 		std::string ApplicationNameL = ApplicationName;
529 		for (size_t i = 0; i < ApplicationNameL.size(); ++i) {
530 			ApplicationNameL[i] = tolower(ApplicationNameL[i]);
531 		}
532 
533 		std::vector <std::string> pixmaps;
534 		pixmaps.push_back(std::string() + PIXMAPS + "/" + FullGameName + ".png");
535 		pixmaps.push_back(std::string() + PIXMAPS + "/" + FullGameNameL + ".png");
536 		pixmaps.push_back(std::string() + "/usr/share/pixmaps" + "/" + FullGameName + ".png");
537 		pixmaps.push_back(std::string() + "/usr/share/pixmaps" + "/" + FullGameNameL + ".png");
538 		pixmaps.push_back(std::string() + PIXMAPS + "/" + ApplicationName + ".png");
539 		pixmaps.push_back(std::string() + PIXMAPS + "/" + ApplicationNameL + ".png");
540 		pixmaps.push_back(std::string() + "/usr/share/pixmaps" + "/" + ApplicationName + ".png");
541 		pixmaps.push_back(std::string() + "/usr/share/pixmaps" + "/" + ApplicationNameL + ".png");
542 		pixmaps.push_back(std::string() + PIXMAPS + "/" + "Stratagus" + ".png");
543 		pixmaps.push_back(std::string() + PIXMAPS + "/" + "stratagus" + ".png");
544 		pixmaps.push_back(std::string() + "/usr/share/pixmaps" + "/" + "Stratagus" + ".png");
545 		pixmaps.push_back(std::string() + "/usr/share/pixmaps" + "/" + "stratagus" + ".png");
546 
547 		for (size_t i = 0; i < pixmaps.size(); ++i) {
548 			if (stat(pixmaps[i].c_str(), &st) == 0) {
549 				if (g) { CGraphic::Free(g); }
550 				g = CGraphic::New(pixmaps[i].c_str());
551 				g->Load();
552 				icon = g->Surface;
553 				if (icon) { break; }
554 			}
555 		}
556 
557 		if (icon) {
558 			SDL_WM_SetIcon(icon, 0);
559 		}
560 
561 		if (g) {
562 			CGraphic::Free(g);
563 		}
564 #endif
565 
566 #if defined(USE_OPENGL) || defined(USE_GLES)
567 		UseOpenGL = UseOpenGL_orig;
568 #endif
569 
570 #endif
571 #ifdef USE_WIN32
572 		HWND hwnd = nullptr;
573 		HICON hicon = nullptr;
574 		SDL_SysWMinfo info;
575 		SDL_VERSION(&info.version);
576 
577 		if (SDL_GetWMInfo(&info)) {
578 			hwnd = info.window;
579 		}
580 
581 		if (hwnd) {
582 			hicon = ExtractIcon(GetModuleHandle(nullptr), Parameters::Instance.applicationName.c_str(), 0);
583 		}
584 
585 		if (hicon) {
586 			SendMessage(hwnd, (UINT)WM_SETICON, ICON_SMALL, (LPARAM)hicon);
587 			SendMessage(hwnd, (UINT)WM_SETICON, ICON_BIG, (LPARAM)hicon);
588 		}
589 #endif
590 	}
591 
592 	// Initialize the display
593 
594 #if !defined(USE_OPENGL) && !defined(USE_GLES)
595 	#ifdef __MORPHOS__
596 	flags = SDL_SWSURFACE;
597 	#else
598 	flags = SDL_HWSURFACE | SDL_HWPALETTE;
599 	#endif
600 #endif
601 
602 	// Sam said: better for windows.
603 	/* SDL_HWSURFACE|SDL_HWPALETTE | */
604 	if (Video.FullScreen) {
605 		flags |= SDL_FULLSCREEN;
606 	}
607 
608 #if defined(USE_OPENGL) || defined(USE_GLES)
609 	if (UseOpenGL) {
610 #ifdef USE_GLES_NATIVE
611 		flags |= SDL_OPENGLES;
612 #endif
613 #ifdef USE_OPENGL
614 		flags |= SDL_OPENGL | SDL_GL_DOUBLEBUFFER;
615 #endif
616 	}
617 #endif
618 
619 	if (!Video.Width || !Video.Height) {
620 		Video.Width = 640;
621 		Video.Height = 480;
622 	}
623 
624 	if (!Video.Depth) {
625 		Video.Depth = 32;
626 	}
627 
628 #if defined(USE_OPENGL) || defined(USE_GLES)
629 	if (!Video.ViewportWidth || !Video.ViewportHeight) {
630 		Video.ViewportWidth = Video.Width;
631 		Video.ViewportHeight = Video.Height;
632 	}
633 	TheScreen = SDL_SetVideoMode(Video.ViewportWidth, Video.ViewportHeight, Video.Depth, flags);
634 #else
635 	TheScreen = SDL_SetVideoMode(Video.Width, Video.Height, Video.Depth, flags);
636 #endif
637 	if (TheScreen && (TheScreen->format->BitsPerPixel != 16
638 					  && TheScreen->format->BitsPerPixel != 32)) {
639 		// Only support 16 and 32 bpp, default to 16
640 #if defined(USE_OPENGL) || defined(USE_GLES)
641 		TheScreen = SDL_SetVideoMode(Video.ViewportWidth, Video.ViewportHeight, 16, flags);
642 #else
643 		TheScreen = SDL_SetVideoMode(Video.Width, Video.Height, 16, flags);
644 #endif
645 	}
646 	if (TheScreen == nullptr) {
647 		fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s\n",
648 				Video.Width, Video.Height, Video.Depth, SDL_GetError());
649 		exit(1);
650 	}
651 
652 	Video.FullScreen = (TheScreen->flags & SDL_FULLSCREEN) ? 1 : 0;
653 	Video.Depth = TheScreen->format->BitsPerPixel;
654 
655 //Wyrmgus start
656 //#if defined(USE_TOUCHSCREEN) && defined(USE_WIN32)
657 //Wyrmgus end
658 	// Must not allow SDL to switch to relative mouse coordinates
659 	// with touchscreen when going fullscreen. So we don't hide the
660 	// cursor, but instead set a transparent 1px cursor
661 	Uint8 emptyCursor[] = {'\0'};
662 	Video.blankCursor = SDL_CreateCursor(emptyCursor, emptyCursor, 1, 1, 0, 0);
663 	SDL_SetCursor(Video.blankCursor);
664 //Wyrmgus start
665 //#else
666 //Wyrmgus end
667 	// Turn cursor off, we use our own.
668 	//Wyrmgus start
669 //	SDL_ShowCursor(SDL_DISABLE);
670 	//Wyrmgus end
671 //Wyrmgus start
672 //#endif
673 //Wyrmgus end
674 
675 	// Make default character translation easier
676 	SDL_EnableUNICODE(1);
677 
678 #if defined(USE_OPENGL) || defined(USE_GLES)
679 	if (UseOpenGL) {
680 #ifdef USE_GLES_EGL
681 		// Get the SDL window handle
682 		SDL_SysWMinfo sysInfo; //Will hold our Window information
683 		SDL_VERSION(&sysInfo.version); //Set SDL version
684 		if (SDL_GetWMInfo(&sysInfo) <= 0) {
685 			fprintf(stderr, "Unable to get window handle\n");
686 			exit(1);
687 		}
688 
689 		eglDisplay = eglGetDisplay((EGLNativeDisplayType)sysInfo.info.x11.display);
690 		if (!eglDisplay) {
691 			fprintf(stderr, "Couldn't open EGL Display\n");
692 			exit(1);
693 		}
694 
695 		if (!eglInitialize(eglDisplay, nullptr, nullptr)) {
696 			fprintf(stderr, "Couldn't initialize EGL Display\n");
697 			exit(1);
698 		}
699 
700 		// Find a matching config
701 		EGLint configAttribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE};
702 		EGLint numConfigsOut = 0;
703 		EGLConfig eglConfig;
704 		if (eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigsOut) != EGL_TRUE || numConfigsOut == 0) {
705 			fprintf(stderr, "Unable to find appropriate EGL config\n");
706 			exit(1);
707 		}
708 
709 		eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)sysInfo.info.x11.window, 0);
710 		if (eglSurface == EGL_NO_SURFACE) {
711 			fprintf(stderr, "Unable to create EGL surface\n");
712 			exit(1);
713 		}
714 
715 		// Bind GLES and create the context
716 		eglBindAPI(EGL_OPENGL_ES_API);
717 		EGLint contextParams[] = {EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE};
718 		EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, nullptr, nullptr);
719 		if (eglContext == EGL_NO_CONTEXT) {
720 			fprintf(stderr, "Unable to create GLES context\n");
721 			exit(1);
722 		}
723 
724 		if (eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) == EGL_FALSE) {
725 			fprintf(stderr, "Unable to make GLES context current\n");
726 			exit(1);
727 		}
728 #endif
729 		InitOpenGL();
730 	}
731 #endif
732 
733 	InitKey2Str();
734 
735 	ColorBlack = Video.MapRGB(TheScreen->format, 0, 0, 0);
736 	ColorDarkGreen = Video.MapRGB(TheScreen->format, 48, 100, 4);
737 	ColorLightBlue = Video.MapRGB(TheScreen->format, 52, 113, 166);
738 	ColorBlue = Video.MapRGB(TheScreen->format, 0, 0, 252);
739 	ColorOrange = Video.MapRGB(TheScreen->format, 248, 140, 20);
740 	ColorWhite = Video.MapRGB(TheScreen->format, 252, 248, 240);
741 	ColorLightGray = Video.MapRGB(TheScreen->format, 192, 192, 192);
742 	ColorGray = Video.MapRGB(TheScreen->format, 128, 128, 128);
743 	ColorDarkGray = Video.MapRGB(TheScreen->format, 64, 64, 64);
744 	ColorRed = Video.MapRGB(TheScreen->format, 252, 0, 0);
745 	ColorGreen = Video.MapRGB(TheScreen->format, 0, 252, 0);
746 	ColorYellow = Video.MapRGB(TheScreen->format, 252, 252, 0);
747 
748 	UI.MouseWarpPos.x = UI.MouseWarpPos.y = -1;
749 }
750 
751 /**
752 **  Check if a resolution is valid
753 **
754 **  @param w  Width
755 **  @param h  Height
756 */
VideoValidResolution(int w,int h)757 int VideoValidResolution(int w, int h)
758 {
759 	return SDL_VideoModeOK(w, h, TheScreen->format->BitsPerPixel, TheScreen->flags);
760 }
761 
762 /**
763 **  Invalidate some area
764 **
765 **  @param x  screen pixel X position.
766 **  @param y  screen pixel Y position.
767 **  @param w  width of rectangle in pixels.
768 **  @param h  height of rectangle in pixels.
769 */
InvalidateArea(int x,int y,int w,int h)770 void InvalidateArea(int x, int y, int w, int h)
771 {
772 #if defined(USE_OPENGL) || defined(USE_GLES)
773 	if (!UseOpenGL)
774 #endif
775 	{
776 		Assert(NumRects != sizeof(Rects) / sizeof(*Rects));
777 		Assert(x >= 0 && y >= 0 && x + w <= Video.Width && y + h <= Video.Height);
778 		Rects[NumRects].x = x;
779 		Rects[NumRects].y = y;
780 		Rects[NumRects].w = w;
781 		Rects[NumRects].h = h;
782 		++NumRects;
783 	}
784 }
785 
786 /**
787 **  Invalidate whole window
788 */
Invalidate()789 void Invalidate()
790 {
791 #if defined(USE_OPENGL) || defined(USE_GLES)
792 	if (!UseOpenGL)
793 #endif
794 	{
795 		Rects[0].x = 0;
796 		Rects[0].y = 0;
797 		Rects[0].w = Video.Width;
798 		Rects[0].h = Video.Height;
799 		NumRects = 1;
800 	}
801 }
802 
803 /**
804 **  Handle interactive input event.
805 **
806 **  @param callbacks  Callback structure for events.
807 **  @param event      SDL event structure pointer.
808 */
SdlDoEvent(const EventCallback & callbacks,SDL_Event & event)809 static void SdlDoEvent(const EventCallback &callbacks, SDL_Event &event)
810 {
811 #if (defined(USE_OPENGL) || defined(USE_GLES))
812 	// Scale mouse-coordinates to viewport
813 	if (ZoomNoResize && (event.type & (SDL_MOUSEBUTTONUP | SDL_MOUSEBUTTONDOWN | SDL_MOUSEMOTION))) {
814 		event.button.x = (Uint16)floorf(event.button.x * float(Video.Width) / Video.ViewportWidth);
815 		event.button.y = (Uint16)floorf(event.button.y * float(Video.Height) / Video.ViewportHeight);
816 		//Wyrmgus start
817 		event.motion.x = (Uint16)floorf(event.motion.x * float(Video.Width) / Video.ViewportWidth);
818 		event.motion.y = (Uint16)floorf(event.motion.y * float(Video.Height) / Video.ViewportHeight);
819 		//Wyrmgus end
820 	}
821 #endif
822 	switch (event.type) {
823 		case SDL_MOUSEBUTTONDOWN:
824 			InputMouseButtonPress(callbacks, SDL_GetTicks(), event.button.button);
825 			break;
826 
827 		case SDL_MOUSEBUTTONUP:
828 			InputMouseButtonRelease(callbacks, SDL_GetTicks(), event.button.button);
829 			break;
830 
831 		// FIXME: check if this is only useful for the cursor
832 		// FIXME: if this is the case we don't need this.
833 		case SDL_MOUSEMOTION:
834 			InputMouseMove(callbacks, SDL_GetTicks(), event.motion.x, event.motion.y);
835 			// FIXME: Same bug fix from X11
836 			if ((UI.MouseWarpPos.x != -1 || UI.MouseWarpPos.y != -1)
837 				&& (event.motion.x != UI.MouseWarpPos.x || event.motion.y != UI.MouseWarpPos.y)) {
838 				int xw = UI.MouseWarpPos.x;
839 				int yw = UI.MouseWarpPos.y;
840 				UI.MouseWarpPos.x = -1;
841 				UI.MouseWarpPos.y = -1;
842 				SDL_WarpMouse(xw, yw);
843 			}
844 			break;
845 
846 		case SDL_ACTIVEEVENT:
847 			if (event.active.state & SDL_APPMOUSEFOCUS) {
848 				static bool InMainWindow = true;
849 
850 				if (InMainWindow && !event.active.gain) {
851 					InputMouseExit(callbacks, SDL_GetTicks());
852 				}
853 				InMainWindow = (event.active.gain != 0);
854 			}
855 			if (!IsNetworkGame() && Preference.PauseOnLeave && (event.active.state & SDL_APPACTIVE || SDL_GetAppState() & SDL_APPACTIVE)) {
856 				static bool DoTogglePause = false;
857 
858 				if (IsSDLWindowVisible && !event.active.gain) {
859 					IsSDLWindowVisible = false;
860 					if (!GamePaused) {
861 						DoTogglePause = true;
862 						UiTogglePause();
863 					}
864 				} else if (!IsSDLWindowVisible && event.active.gain) {
865 					IsSDLWindowVisible = true;
866 					if (GamePaused && DoTogglePause) {
867 						DoTogglePause = false;
868 						UiTogglePause();
869 					}
870 				}
871 			}
872 			break;
873 
874 		case SDL_KEYDOWN:
875 #if (defined(USE_OPENGL) || defined(USE_GLES))
876 			if (GLShaderPipelineSupported
877 				&& event.key.keysym.sym == SDLK_SLASH
878 				&& event.key.keysym.mod & KMOD_ALT
879 				&& event.key.keysym.mod & KMOD_CTRL) {
880 				LoadShaders();
881 				break;
882 			}
883 #endif
884 			InputKeyButtonPress(callbacks, SDL_GetTicks(),
885 								event.key.keysym.sym, event.key.keysym.unicode);
886 			break;
887 
888 		case SDL_KEYUP:
889 			InputKeyButtonRelease(callbacks, SDL_GetTicks(),
890 								  event.key.keysym.sym, event.key.keysym.unicode);
891 			break;
892 
893 		case SDL_QUIT:
894 			Exit(0);
895 			break;
896 	}
897 
898 	if (&callbacks == GetCallbacks()) {
899 		handleInput(&event);
900 	}
901 }
902 
ValidateOpenGLScreen()903 void ValidateOpenGLScreen()
904 {
905 	if (RegenerateScreen) {
906 		Video.ResizeScreen(Video.Width, Video.Height);
907 		RegenerateScreen = false;
908 	}
909 }
910 
911 /**
912 **  Set the current callbacks
913 */
SetCallbacks(const EventCallback * callbacks)914 void SetCallbacks(const EventCallback *callbacks)
915 {
916 	Callbacks = callbacks;
917 }
918 
919 /**
920 **  Get the current callbacks
921 */
GetCallbacks()922 const EventCallback *GetCallbacks()
923 {
924 	return Callbacks;
925 }
926 
PollEvent()927 int PollEvent()
928 {
929 	SDL_Event event;
930 	if (SDL_PollEvent(&event)) { // Handle SDL event
931 		SdlDoEvent(*GetCallbacks(), event);
932 		return 1;
933 	}
934 
935 	return 0;
936 }
937 
PollEvents()938 void PollEvents()
939 {
940 	if (Callbacks == nullptr) return;
941 
942 	while (PollEvent()) { }
943 }
944 
945 /**
946 **  Wait for interactive input event for one frame.
947 **
948 **  Handles system events, joystick, keyboard, mouse.
949 **  Handles the network messages.
950 **  Handles the sound queue.
951 **
952 **  All events available are fetched. Sound and network only if available.
953 **  Returns if the time for one frame is over.
954 */
WaitEventsOneFrame()955 void WaitEventsOneFrame()
956 {
957 	++FrameCounter;
958 
959 	Uint32 ticks = SDL_GetTicks();
960 	if (ticks > NextFrameTicks) { // We are too slow :(
961 		++SlowFrameCounter;
962 	}
963 
964 	InputMouseTimeout(*GetCallbacks(), ticks);
965 	InputKeyTimeout(*GetCallbacks(), ticks);
966 	CursorAnimate(ticks);
967 
968 	int interrupts = 0;
969 
970 	for (;;) {
971 		// Time of frame over? This makes the CPU happy. :(
972 		ticks = SDL_GetTicks();
973 		if (!interrupts && ticks < NextFrameTicks) {
974 			SDL_Delay(NextFrameTicks - ticks);
975 			ticks = SDL_GetTicks();
976 		}
977 		while (ticks >= (unsigned long)(NextFrameTicks)) {
978 			++interrupts;
979 			NextFrameTicks += FrameTicks;
980 		}
981 
982 		int i = PollEvent();
983 
984 		// Network
985 		int s = 0;
986 		if (IsNetworkGame()) {
987 			s = NetworkFildes.HasDataToRead(0);
988 			if (s > 0) {
989 				GetCallbacks()->NetworkEvent();
990 			}
991 		}
992 		// No more input and time for frame over: return
993 		if (!i && s <= 0 && interrupts) {
994 			break;
995 		}
996 	}
997 	handleInput(nullptr);
998 
999 	if (!SkipGameCycle--) {
1000 		SkipGameCycle = SkipFrames;
1001 	}
1002 }
1003 
1004 /**
1005 **  Realize video memory.
1006 */
RealizeVideoMemory()1007 void RealizeVideoMemory()
1008 {
1009 #if defined(USE_OPENGL) || defined(USE_GLES)
1010 	if (UseOpenGL) {
1011 #ifdef USE_GLES_EGL
1012 		eglSwapBuffers(eglDisplay, eglSurface);
1013 #endif
1014 #if defined(USE_OPENGL) || defined(USE_GLES_NATIVE)
1015 		if (GLShaderPipelineSupported) {
1016 			RenderFramebufferToScreen();
1017 		} else {
1018 			SDL_GL_SwapBuffers();
1019 		}
1020 #endif
1021 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1022 	} else
1023 #endif
1024 	{
1025 		if (NumRects) {
1026 			SDL_UpdateRects(TheScreen, NumRects, Rects);
1027 			NumRects = 0;
1028 		}
1029 	}
1030 	HideCursor();
1031 }
1032 
1033 /**
1034 **  Lock the screen for write access.
1035 */
SdlLockScreen()1036 void SdlLockScreen()
1037 {
1038 #if defined(USE_OPENGL) || defined(USE_GLES)
1039 	if (!UseOpenGL)
1040 #endif
1041 	{
1042 		if (SDL_MUSTLOCK(TheScreen)) {
1043 			SDL_LockSurface(TheScreen);
1044 		}
1045 	}
1046 }
1047 
1048 /**
1049 **  Unlock the screen for write access.
1050 */
SdlUnlockScreen()1051 void SdlUnlockScreen()
1052 {
1053 #if defined(USE_OPENGL) || defined(USE_GLES)
1054 	if (!UseOpenGL)
1055 #endif
1056 	{
1057 		if (SDL_MUSTLOCK(TheScreen)) {
1058 			SDL_UnlockSurface(TheScreen);
1059 		}
1060 	}
1061 }
1062 
1063 /**
1064 **  Convert a SDLKey to a string
1065 */
SdlKey2Str(int key)1066 const char *SdlKey2Str(int key)
1067 {
1068 	return Key2Str[key].c_str();
1069 }
1070 
1071 /**
1072 **  Convert a string to SDLKey
1073 */
Str2SdlKey(const char * str)1074 int Str2SdlKey(const char *str)
1075 {
1076 	InitKey2Str();
1077 
1078 	std::map<int, std::string>::iterator i;
1079 	for (i = Key2Str.begin(); i != Key2Str.end(); ++i) {
1080 		if (!strcasecmp(str, (*i).second.c_str())) {
1081 			return (*i).first;
1082 		}
1083 	}
1084 	std::map<std::string, int>::iterator i2;
1085 	for (i2 = Str2Key.begin(); i2 != Str2Key.end(); ++i2) {
1086 		if (!strcasecmp(str, (*i2).first.c_str())) {
1087 			return (*i2).second;
1088 		}
1089 	}
1090 	return 0;
1091 }
1092 
1093 /**
1094 **  Check if the mouse is grabbed
1095 */
SdlGetGrabMouse()1096 bool SdlGetGrabMouse()
1097 {
1098 	return SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON;
1099 }
1100 
1101 /**
1102 **  Toggle grab mouse.
1103 **
1104 **  @param mode  Wanted mode, 1 grab, -1 not grab, 0 toggle.
1105 */
ToggleGrabMouse(int mode)1106 void ToggleGrabMouse(int mode)
1107 {
1108 	bool grabbed = SdlGetGrabMouse();
1109 
1110 	if (mode <= 0 && grabbed) {
1111 		SDL_WM_GrabInput(SDL_GRAB_OFF);
1112 	} else if (mode >= 0 && !grabbed) {
1113 		SDL_WM_GrabInput(SDL_GRAB_ON);
1114 	}
1115 }
1116 
1117 /**
1118 **  Toggle full screen mode.
1119 */
ToggleFullScreen()1120 void ToggleFullScreen()
1121 {
1122 #if defined(USE_WIN32) || defined(__APPLE__)
1123 	long framesize;
1124 	SDL_Rect clip;
1125 	Uint32 flags;
1126 	int w;
1127 	int h;
1128 	int bpp;
1129 	unsigned char *pixels = nullptr;
1130 	SDL_Color *palette = nullptr;
1131 	int ncolors = 0;
1132 
1133 	if (!TheScreen) { // don't bother if there's no surface.
1134 		return;
1135 	}
1136 
1137 	flags = TheScreen->flags;
1138 	w = TheScreen->w;
1139 	h = TheScreen->h;
1140 	bpp = TheScreen->format->BitsPerPixel;
1141 
1142 	if (!SDL_VideoModeOK(w, h, bpp,	flags ^ SDL_FULLSCREEN)) {
1143 		return;
1144 	}
1145 
1146 	SDL_GetClipRect(TheScreen, &clip);
1147 
1148 	// save the contents of the screen.
1149 	framesize = w * h * TheScreen->format->BytesPerPixel;
1150 
1151 #if defined(USE_OPENGL) || defined(USE_GLES)
1152 	if (!UseOpenGL)
1153 #endif
1154 	{
1155 		if (!(pixels = new unsigned char[framesize])) { // out of memory
1156 			return;
1157 		}
1158 		SDL_LockSurface(TheScreen);
1159 		memcpy(pixels, TheScreen->pixels, framesize);
1160 
1161 		if (TheScreen->format->palette) {
1162 			ncolors = TheScreen->format->palette->ncolors;
1163 			if (!(palette = new SDL_Color[ncolors])) {
1164 				delete[] pixels;
1165 				return;
1166 			}
1167 			memcpy(palette, TheScreen->format->palette->colors,
1168 				   ncolors * sizeof(SDL_Color));
1169 		}
1170 		SDL_UnlockSurface(TheScreen);
1171 	}
1172 
1173 	TheScreen = SDL_SetVideoMode(w, h, bpp, flags ^ SDL_FULLSCREEN);
1174 	if (!TheScreen) {
1175 		TheScreen = SDL_SetVideoMode(w, h, bpp, flags);
1176 		if (!TheScreen) { // completely screwed.
1177 #if defined(USE_OPENGL) || defined(USE_GLES)
1178 			if (!UseOpenGL)
1179 #endif
1180 			{
1181 				delete[] pixels;
1182 				delete[] palette;
1183 			}
1184 			fprintf(stderr, "Toggle to fullscreen, crashed all\n");
1185 			Exit(-1);
1186 		}
1187 	}
1188 
1189 #ifndef USE_TOUCHSCREEN
1190 	// Cannot hide cursor on Windows with touchscreen, as it switches
1191 	// to relative mouse coordinates in fullscreen. See above initial
1192 	// call to ShowCursor
1193 	//
1194 	// Windows shows the SDL cursor when starting in fullscreen mode
1195 	// then switching to window mode.  This hides the cursor again.
1196 	//Wyrmgus start
1197 //	SDL_ShowCursor(SDL_ENABLE);
1198 //	SDL_ShowCursor(SDL_DISABLE);
1199 	//Wyrmgus end
1200 #endif
1201 
1202 #if defined(USE_OPENGL) || defined(USE_GLES)
1203 	if (UseOpenGL) {
1204 		ReloadOpenGL();
1205 	} else
1206 #endif
1207 	{
1208 		SDL_LockSurface(TheScreen);
1209 		memcpy(TheScreen->pixels, pixels, framesize);
1210 		delete[] pixels;
1211 
1212 		if (TheScreen->format->palette) {
1213 			// !!! FIXME : No idea if that flags param is right.
1214 			SDL_SetPalette(TheScreen, SDL_LOGPAL, palette, 0, ncolors);
1215 			delete[] palette;
1216 		}
1217 		SDL_UnlockSurface(TheScreen);
1218 	}
1219 
1220 	SDL_SetClipRect(TheScreen, &clip);
1221 
1222 	Invalidate(); // Update display
1223 #else // !USE_WIN32
1224 	SDL_WM_ToggleFullScreen(TheScreen);
1225 #endif
1226 
1227 	Video.FullScreen = (TheScreen->flags & SDL_FULLSCREEN) ? 1 : 0;
1228 }
1229 
1230 //@}
1231