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