1 #include "SDLGUI.h"
2 
3 #include <GG/WndEvent.h>
4 #include <GG/utf8/checked.h>
5 
6 #include <cctype>
7 #include <iostream>
8 
9 #include <boost/format.hpp>
10 
11 using namespace GG;
12 
13 namespace {
GetSDLModKeys()14     Flags<ModKey> GetSDLModKeys() {
15         Flags<ModKey> retval;
16         Uint32 sdl_keys = SDL_GetModState();
17         if (sdl_keys & KMOD_LSHIFT) retval |= MOD_KEY_LSHIFT;
18         if (sdl_keys & KMOD_RSHIFT) retval |= MOD_KEY_RSHIFT;
19         if (sdl_keys & KMOD_LCTRL)  retval |= MOD_KEY_LCTRL;
20         if (sdl_keys & KMOD_RCTRL)  retval |= MOD_KEY_RCTRL;
21         if (sdl_keys & KMOD_LALT)   retval |= MOD_KEY_LALT;
22         if (sdl_keys & KMOD_RALT)   retval |= MOD_KEY_RALT;
23         if (sdl_keys & KMOD_LGUI)   retval |= MOD_KEY_LMETA;
24         if (sdl_keys & KMOD_RGUI)   retval |= MOD_KEY_RMETA;
25         if (sdl_keys & KMOD_NUM)    retval |= MOD_KEY_NUM;
26         if (sdl_keys & KMOD_CAPS)   retval |= MOD_KEY_CAPS;
27         if (sdl_keys & KMOD_MODE)   retval |= MOD_KEY_MODE;
28         return retval;
29     }
30 
SetSDLFullscreenSize(SDL_Window * window,int display_id,int width,int height)31     Pt SetSDLFullscreenSize(SDL_Window* window, int display_id, int width, int height) {
32         SDL_DisplayMode target;
33         target.w = width;
34         target.h = height;
35         target.format = 0; // DOn't care
36         target.driverdata = nullptr;
37         target.refresh_rate = 0;
38         SDL_DisplayMode closest;
39         SDL_GetClosestDisplayMode(display_id, &target, &closest);
40         SDL_SetWindowDisplayMode(window, &closest);
41         return Pt(X(closest.w), Y(closest.h));
42     }
43 
Enter2DModeImpl(int width,int height)44     void Enter2DModeImpl(int width, int height) {
45         glPushAttrib(GL_ENABLE_BIT | GL_PIXEL_MODE_BIT | GL_TEXTURE_BIT);
46         glDisable(GL_DEPTH_TEST);
47         glDisable(GL_LIGHTING);
48         glDisable(GL_CULL_FACE);
49         glEnable(GL_TEXTURE_2D);
50 
51         glEnable(GL_BLEND);
52         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
53 
54         glViewport(0, 0, width, height);
55 
56         glMatrixMode(GL_PROJECTION);
57         glPushMatrix();
58         glLoadIdentity();
59 
60         // This sets up the world coordinate space with the origin in the
61         // upper-left corner and +x and +y directions right and down,
62         // respectively.
63         glOrtho(0.0, width, height, 0.0, -100.0, 100.0);
64 
65         glMatrixMode(GL_MODELVIEW);
66         glPushMatrix();
67         glLoadIdentity();
68 
69         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
70     }
71 
72     struct QuitSignal {
QuitSignal__anonfcbddf6a0111::QuitSignal73         QuitSignal(int exit_code_) :
74             exit_code(exit_code_)
75         {}
76 
77         int exit_code;
78     };
79 
80     class FramebufferFailedException : public std::exception {
81     public:
FramebufferFailedException(GLenum status)82         FramebufferFailedException(GLenum status):
83             m_status(status)
84         {}
85 
what() const86         const char* what() const noexcept override {
87             switch (m_status) {
88                 case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
89                     return "The requested framebuffer format was unsupported";
90                 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
91                     return "One of the framebuffer attachments is incomplete.";
92                 case GL_FRAMEBUFFER_UNDEFINED:
93                     return "The default framebuffer does not exist.";
94                 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
95                     return "At least one picture must be attached to the framebuffer.";
96                 case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
97                     return "All populated color attachments are not from textures of the same target.";
98                 default:
99                     return "Framebuffer creation failed with an unhandled exception.";
100             }
101         }
102     private:
103         GLenum m_status;
104     };
105 }
106 
107 class Framebuffer {
108 public:
109     /// Construct a framebuffer of dimensions \a size.
110     /// \throws FramebufferFailedException if using framebuffers is not going to work.
Framebuffer(GG::Pt size)111     Framebuffer(GG::Pt size) :
112         m_id(0),
113         m_texture(0),
114         m_depth_rbo(0)
115     {
116         int width = Value(size.x);
117         int height = Value(size.y);
118 
119         // Create the texture to render the image on
120         glGenTextures(1, &m_texture);
121         glBindTexture(GL_TEXTURE_2D, m_texture);
122         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
123         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
124         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
125         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
126         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
127         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0,
128                         GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
129         glBindTexture(GL_TEXTURE_2D, 0);
130 
131         // create a renderbuffer object to store depth and stencil info
132         glGenRenderbuffersEXT(1, &m_depth_rbo);
133         glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depth_rbo);
134         glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, width, height);
135         glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
136 
137         glGenFramebuffersEXT(1, &m_id);
138         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_id);
139 
140         // attach the texture to FBO color attachment point
141         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,        // 1. fbo target: GL_FRAMEBUFFER_EXT
142                                     GL_COLOR_ATTACHMENT0_EXT,  // 2. attachment point
143                                     GL_TEXTURE_2D,         // 3. tex target: GL_TEXTURE_2D
144                                     m_texture,             // 4. tex ID
145                                     0);                    // 5. mipmap level: 0(base)
146 
147         // attach the renderbuffer to depth attachment point
148         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,     // 1. fbo target: GL_FRAMEBUFFER_EXT
149                                         GL_DEPTH_ATTACHMENT_EXT,
150                                         GL_RENDERBUFFER_EXT,     // 3. rbo target: GL_RENDERBUFFER_EXT
151                                         m_depth_rbo);              // 4. rbo ID
152 
153         // the same render buffer has the stencil data in other bits
154         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
155                                     GL_STENCIL_ATTACHMENT_EXT,
156                                     GL_RENDERBUFFER_EXT,
157                                     m_depth_rbo);
158 
159         // check FBO status
160         GLenum status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
161         if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
162             throw FramebufferFailedException (status);
163         }
164 
165         // switch back to window-system-provided framebuffer
166         glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
167     }
168 
OpenGLId()169     GLuint OpenGLId()
170     { return m_id; }
171 
TextureId()172     GLuint TextureId()
173     { return m_texture; }
174 
~Framebuffer()175     ~Framebuffer() {
176         glDeleteFramebuffersEXT(1, &m_id);
177         glDeleteRenderbuffersEXT(1, &m_depth_rbo);
178         glDeleteTextures(1, &m_texture);
179     }
180 
181 private:
182     GLuint m_id;
183     GLuint m_texture;
184     GLuint m_depth_rbo;
185 };
186 
187 // member functions
SDLGUI(int w,int h,bool calc_FPS,const std::string & app_name,int x,int y,bool fullscreen,bool fake_mode_change)188 SDLGUI::SDLGUI(int w/* = 1024*/, int h/* = 768*/, bool calc_FPS/* = false*/,
189                const std::string& app_name/* = "GG"*/,
190                int x, int y, bool fullscreen, bool fake_mode_change) :
191     GUI(app_name),
192     m_app_width(w),
193     m_app_height(h),
194     m_initial_x(x),
195     m_initial_y(y),
196     m_fullscreen(fullscreen),
197     m_fake_mode_change(fake_mode_change),
198     m_display_id(0),
199     m_window(nullptr),
200     m_gl_context(nullptr),
201     m_done(false),
202     m_framebuffer(nullptr)
203 {
204     SDLInit();
205 }
206 
~SDLGUI()207 SDLGUI::~SDLGUI()
208 { SDLQuit(); }
209 
AppWidth() const210 X SDLGUI::AppWidth() const
211 { return m_app_width; }
212 
AppHeight() const213 Y SDLGUI::AppHeight() const
214 { return m_app_height; }
215 
Ticks() const216 unsigned int SDLGUI::Ticks() const
217 { return SDL_GetTicks(); }
218 
Fullscreen() const219 bool SDLGUI::Fullscreen() const
220 { return m_fullscreen; }
221 
FakeModeChange() const222 bool SDLGUI::FakeModeChange() const
223 { return m_fake_mode_change; }
224 
ClipboardText() const225 std::string SDLGUI::ClipboardText() const {
226     if (SDL_HasClipboardText()) {
227         char* text = SDL_GetClipboardText();
228         if (text) {
229             std::string result(text);
230             SDL_free(text);
231             return result;
232         }
233     }
234 
235     return std::string();
236 }
237 
ExitApp(int code)238 void SDLGUI::ExitApp(int code)
239 { throw QuitSignal(code); }
240 
SetWindowTitle(const std::string & title)241 void SDLGUI::SetWindowTitle(const std::string& title)
242 { SDL_SetWindowTitle(m_window, title.c_str()); }
243 
SetVideoMode(X width,Y height,bool fullscreen,bool fake_mode_change)244 void SDLGUI::SetVideoMode(X width, Y height, bool fullscreen, bool fake_mode_change)
245 {
246     m_fullscreen = fullscreen;
247     // Only allow fake mode change if the necessary extensions are supported
248     m_fake_mode_change = fake_mode_change && FramebuffersAvailable();
249     m_app_width = width;
250     m_app_height = height;
251     SDL_SetWindowFullscreen(m_window, 0);
252     glViewport(0, 0, Value(width), Value(height));
253     if (fullscreen) {
254         if (!m_fake_mode_change) {
255             Pt resulting_size = SetSDLFullscreenSize(m_window, m_display_id, Value(width), Value(height));
256             m_app_width = resulting_size.x;
257             m_app_height = resulting_size.y;
258         }
259         SDL_SetWindowFullscreen(m_window, m_fake_mode_change?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_FULLSCREEN);
260     } else {
261         SDL_SetWindowSize(m_window, Value(width), Value(height));
262         SDL_RestoreWindow(m_window);
263     }
264     ResetFramebuffer();
265 }
266 
SetClipboardText(const std::string & text)267 bool SDLGUI::SetClipboardText(const std::string& text)
268 { return SDL_SetClipboardText(text.c_str()) == 0; }
269 
GetGUI()270 SDLGUI* SDLGUI::GetGUI()
271 { return dynamic_cast<SDLGUI*>(GUI::GetGUI()); }
272 
SetAppSize(const Pt & size)273 void SDLGUI::SetAppSize(const Pt& size) {
274     m_app_width = size.x;
275     m_app_height = size.y;
276 }
277 
SDLInit()278 void SDLGUI::SDLInit() {
279     SDLMinimalInit();
280 
281     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
282     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 2);
283     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
284     SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
285 
286     // Create the window with a temporary size.
287     // Use the same code for the real size initialization that is used for resizing the window
288     // to avoid duplicated effort.
289     m_window = SDL_CreateWindow(AppName().c_str(), Value(m_initial_x), Value(m_initial_y),
290                                 Value(m_app_width), Value(m_app_height), SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN);
291     if (m_window)
292         m_gl_context = SDL_GL_CreateContext(m_window);
293     GLenum glew_status = glewInit();
294 
295     if (!m_window || !m_gl_context || GLEW_OK != glew_status) {
296         std::string msg;
297         if (!m_window) {
298             msg = "Unable to create window.";
299             msg += "\n\nSDL reported:\n";
300             msg += SDL_GetError();
301         } else if (!m_gl_context) {
302             msg = "Unable to create accelerated OpenGL 2.0 context.";
303             msg += "\n\nSDL reported:\n";
304             msg += SDL_GetError();
305         } else {
306             msg = "Unable to load OpenGL entry points.";
307             msg += "\n\nGLEW reported:\n";
308             msg += reinterpret_cast<const char*>(glewGetErrorString(glew_status));
309         }
310 
311         SDL_ShowSimpleMessageBox(
312             SDL_MESSAGEBOX_ERROR, "OpenGL initialization error", msg.c_str(), nullptr);
313         std::cerr << msg << std::endl;
314         ExitApp(1);
315     }
316 
317     SDL_ShowWindow(m_window);
318 
319     SDL_ShowCursor(false);
320 
321     ResetFramebuffer();
322 
323     GLInit();
324 
325     // Now we can use the standard resizing call to make our window the size it belongs.
326     SetVideoMode(m_app_width, m_app_height, m_fullscreen, m_fake_mode_change);
327 
328     SDL_EnableScreenSaver();
329 }
330 
GLInit()331 void SDLGUI::GLInit() {
332     glEnable(GL_LIGHTING);
333     glEnable(GL_LIGHT0);
334     glEnable(GL_DEPTH_TEST);
335     glEnable(GL_CULL_FACE);
336     glEnable(GL_BLEND);
337     glShadeModel(GL_SMOOTH);
338     glClearColor(0, 0, 0, 0);
339     glViewport(0, 0, Value(m_app_width), Value(m_app_height));
340     glMatrixMode(GL_PROJECTION);
341     glLoadIdentity();
342 
343     // set up perspective with vertical FOV of 50°. 1:1 application
344     // window ratio, near plane of 1.0 and far plane of 10.0
345     float ratio = Value(m_app_width) * 1.0f / Value(m_app_height);
346     float radians = static_cast<float>(50.0 * M_PI / 180.0);
347     float near = 1.0f;
348     float far = 10.0f;
349     float cotangent = std::cos(radians) / std::sin(radians);
350 
351     float projection[4][4] = {};
352     projection[0][0] = cotangent / ratio;
353     projection[1][1] = cotangent;
354     projection[2][2] = -((far + near) / (far - near));
355     projection[2][3] = -1.0f;
356     projection[3][2] = -((2.0f * far * near) / (far - near));
357     projection[3][3] = 0.0f;
358 
359     glMultMatrixf(&projection[0][0]);
360 }
361 
HandleSystemEvents()362 void SDLGUI::HandleSystemEvents() {
363     // handle events
364     SDL_Event event;
365     while (0 < SDL_PollEvent(&event)) {
366         bool send_to_gg = false;
367         EventType gg_event = MOUSEMOVE;
368         Key key = GGK_NONE;
369         std::uint32_t key_code_point = 0;
370         Flags<ModKey> mod_keys = GetSDLModKeys();
371         // In GiGi some events contain mouse position info,
372         // where the corresponding sdl event does not.
373         // Therefore we need to get the position,
374         int mouse_x = 0;
375         int mouse_y = 0;
376         SDL_GetMouseState(&mouse_x, &mouse_y);
377         Pt mouse_pos = Pt(X(mouse_x), Y(mouse_y));
378         Pt mouse_rel(X(event.motion.xrel), Y(event.motion.yrel));
379 
380         switch (event.type) {
381         case SDL_KEYDOWN:
382         case SDL_KEYUP:
383             send_to_gg = true;
384             key = static_cast<Key>(event.key.keysym.scancode);
385             key_code_point = event.key.keysym.sym;
386             gg_event = (event.type == SDL_KEYDOWN) ? KEYPRESS : KEYRELEASE;
387             break;
388 
389         case SDL_TEXTINPUT:
390             RelayTextInput(event.text, mouse_pos);  // calls HandleGGEvent repeatedly to process
391             break;
392 
393         case SDL_MOUSEMOTION:
394             send_to_gg = true;
395             gg_event = MOUSEMOVE;
396             break;
397 
398         case SDL_MOUSEBUTTONDOWN:
399             send_to_gg = true;
400             switch (event.button.button) {
401                 case SDL_BUTTON_LEFT:      gg_event = LPRESS; break;
402                 case SDL_BUTTON_MIDDLE:    gg_event = MPRESS; break;
403                 case SDL_BUTTON_RIGHT:     gg_event = RPRESS; break;
404             }
405             mod_keys = GetSDLModKeys();
406             break;
407 
408         case SDL_MOUSEBUTTONUP:
409             send_to_gg = true;
410             switch (event.button.button) {
411                 case SDL_BUTTON_LEFT:   gg_event = LRELEASE; break;
412                 case SDL_BUTTON_MIDDLE: gg_event = MRELEASE; break;
413                 case SDL_BUTTON_RIGHT:  gg_event = RRELEASE; break;
414             }
415             mod_keys = GetSDLModKeys();
416             break;
417 
418         case SDL_MOUSEWHEEL:
419             send_to_gg = true;
420             gg_event = MOUSEWHEEL;
421             mouse_rel = Pt(X(event.wheel.x), Y(event.wheel.y));
422             mod_keys = GetSDLModKeys();
423             break;
424 
425         case SDL_WINDOWEVENT:
426             send_to_gg = false;
427             switch (event.window.event) {
428                 case SDL_WINDOWEVENT_SIZE_CHANGED:
429                     // Alt-tabbing and other things give dubious resize events while in fullscreen mode.
430                     // ignore them
431                     if (!m_fullscreen) {
432                         m_app_width = X(event.window.data1);
433                         m_app_height = Y(event.window.data2);
434                     }
435                     // If faking resolution change, we need to listen to this event
436                     // to size the buffer correctly for the screen.
437                     if (m_fullscreen && m_fake_mode_change) {
438                         ResetFramebuffer();
439                     }
440                 case SDL_WINDOWEVENT_RESIZED:
441                     // Alt-tabbing and other things give dubious resize events while in fullscreen mode.
442                     // ignore them
443                     if (!m_fullscreen) {
444                         WindowResizedSignal(X(event.window.data1), Y(event.window.data2));
445                     }
446                     break;
447                 case SDL_WINDOWEVENT_FOCUS_GAINED:
448                     FocusChangedSignal(true);
449                     break;
450                 case SDL_WINDOWEVENT_FOCUS_LOST:
451                     FocusChangedSignal(false);
452                     break;
453                 case SDL_WINDOWEVENT_MOVED:
454                     WindowMovedSignal(X(event.window.data1), Y(event.window.data2));
455                     break;
456                 case SDL_WINDOWEVENT_MINIMIZED:
457                 case SDL_WINDOWEVENT_MAXIMIZED:
458                 case SDL_WINDOWEVENT_RESTORED:
459                     break;
460                 case SDL_WINDOWEVENT_CLOSE:
461                     WindowClosingSignal();
462                     break;
463             }
464             break;
465         }
466 
467         if (send_to_gg)
468             HandleGGEvent(gg_event, key, key_code_point, mod_keys, mouse_pos, mouse_rel);
469         else
470             HandleNonGGEvent(event);
471     }
472 }
473 
HandleNonGGEvent(const SDL_Event & event)474 void SDLGUI::HandleNonGGEvent(const SDL_Event& event) {
475     switch (event.type) {
476     case SDL_QUIT:
477         AppQuittingSignal();
478         break;
479     }
480 }
481 
RenderBegin()482 void SDLGUI::RenderBegin() {
483     if (m_fake_mode_change && m_fullscreen) {
484         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_framebuffer->OpenGLId());
485     }
486     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
487     glViewport(0, 0, Value(m_app_width), Value(m_app_height));
488 }
489 
RenderEnd()490 void SDLGUI::RenderEnd() {
491     if (m_fake_mode_change && m_fullscreen) {
492         // Return to rendering on the real screen
493         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
494         // Clear the real screen
495         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
496         int width, height;
497         SDL_GetWindowSize(m_window, &width, &height);
498         Enter2DModeImpl(width, height);
499         // Disable blending, we want a direct copy
500         glDisable(GL_BLEND);
501         // Draw the virtual screen on the real screen
502         glBindTexture(GL_TEXTURE_2D, m_framebuffer->TextureId());
503         glEnable(GL_TEXTURE_2D);
504         glBegin(GL_QUADS);
505             glTexCoord2f(0.0, 1.0);
506             glVertex2i(0, 0);
507             glTexCoord2f(1.0, 1.0);
508             glVertex2i(width, 0);
509             glTexCoord2f(1.0, 0.0);
510             glVertex2i(width, height);
511             glTexCoord2f(0.0, 0.0);
512             glVertex2i(0, height);
513         glEnd();
514         glEnable(GL_BLEND);
515         Exit2DMode();
516     }
517     SDL_GL_SwapWindow(m_window);
518 }
519 
FinalCleanup()520 void SDLGUI::FinalCleanup() {
521     if (m_gl_context){
522         SDL_GL_DeleteContext(m_gl_context);
523         m_gl_context = nullptr;
524     }
525 
526     if (m_window) {
527         SDL_DestroyWindow(m_window);
528         m_window = nullptr;
529     }
530 }
531 
SDLQuit()532 void SDLGUI::SDLQuit() {
533     // Ensure that the dektop resolution is restored on linux
534     // By returning to windowed mode before exit.
535     SetVideoMode(m_app_width, m_app_height, false, false);
536     FinalCleanup();
537     SDL_Quit();
538 }
539 
Run()540 void SDLGUI::Run() {
541     try {
542         Initialize();
543         RunModal(nullptr, m_done);
544     } catch (const QuitSignal& e) {
545         if (e.exit_code != 0)
546             throw;
547 
548         // This is the normal exit path from Run()
549         // Do not put exit(0) or ExitApp(0) here.
550         return;
551     }
552 }
553 
AppHasMouseFocus() const554 bool SDLGUI::AppHasMouseFocus() const {
555     auto window_flags = SDL_GetWindowFlags(m_window);
556     return window_flags & SDL_WINDOW_MOUSE_FOCUS;
557 }
558 
GetSupportedResolutions() const559 std::vector<std::string> SDLGUI::GetSupportedResolutions() const {
560     std::vector<std::string> mode_vec;
561 
562     SDLMinimalInit();
563 
564     unsigned valid_mode_count = SDL_GetNumDisplayModes(m_display_id);
565 
566     /* Check if our resolution is restricted */
567     if ( valid_mode_count < 1 ) {
568         // This is bad.
569     } else {
570         for (unsigned i = 0; i < valid_mode_count; ++i) {
571             SDL_DisplayMode mode;
572             if (SDL_GetDisplayMode(m_display_id, i, &mode) != 0) {
573                 SDL_Log("SDL_GetDisplayMode failed: %s", SDL_GetError());
574             } else {
575                 mode_vec.push_back(boost::io::str(boost::format("%1% x %2%") % mode.w % mode.h));
576             }
577         }
578     }
579 
580     return mode_vec;
581 }
582 
SDLMinimalInit()583 void SDLGUI::SDLMinimalInit() {
584     if (!SDL_WasInit(SDL_INIT_VIDEO)) {
585         if (SDL_Init(SDL_INIT_VIDEO) < 0) {
586             std::cerr << "SDL initialization failed: " << SDL_GetError() << std::endl;
587             throw std::runtime_error("Failed to initialize SDL");
588         }
589     }
590 }
591 
GetDefaultResolution(int display_id) const592 Pt SDLGUI::GetDefaultResolution(int display_id) const
593 { return GetDefaultResolutionStatic(display_id); }
594 
GetDefaultResolutionStatic(int display_id)595 Pt SDLGUI::GetDefaultResolutionStatic(int display_id) {
596     SDLMinimalInit();
597 
598     if (display_id >= 0 && display_id < SDL_GetNumVideoDisplays()) {
599         SDL_DisplayMode mode;
600         SDL_GetDesktopDisplayMode(display_id, &mode);
601         Pt resolution(X(mode.w), Y(mode.h));
602         return resolution;
603     } else {
604         return Pt(X0, Y0);
605     }
606 }
607 
NumVideoDisplaysStatic()608 int SDLGUI::NumVideoDisplaysStatic() {
609     SDLMinimalInit();
610     return SDL_GetNumVideoDisplays();
611 }
612 
MaximumPossibleDimension(bool is_width)613 int SDLGUI::MaximumPossibleDimension(bool is_width) {
614     int dim = 0;
615 
616     int num_displays = NumVideoDisplaysStatic();
617     for (int i_display = 0; i_display < num_displays; ++i_display) {
618         SDL_Rect r;
619         if (SDL_GetDisplayBounds(i_display, &r) == 0) {
620             dim += is_width ? r.w : r.h;
621         }
622     }
623     return dim;
624 }
625 
MaximumPossibleWidth()626 int SDLGUI::MaximumPossibleWidth()
627 { return MaximumPossibleDimension(true); }
628 
MaximumPossibleHeight()629 int SDLGUI::MaximumPossibleHeight()
630 { return MaximumPossibleDimension(false); }
631 
RelayTextInput(const SDL_TextInputEvent & text,GG::Pt mouse_pos)632 void SDLGUI::RelayTextInput(const SDL_TextInputEvent& text, GG::Pt mouse_pos) {
633     const char *current = text.text;
634     const char *last = current;
635     // text is zero terminated, find the end
636     while (*last)
637     { ++last; }
638     std::string text_string(current, last);
639 
640     // pass each utf-8 character as a separate event
641     while (current != last) {
642         HandleGGEvent(TEXTINPUT, GGK_NONE, utf8::next(current, last), Flags<ModKey>(),
643                       mouse_pos, Pt(X0, Y0), &text_string);
644     }
645 }
646 
ResetFramebuffer()647 void SDLGUI::ResetFramebuffer() {
648     m_framebuffer.reset();
649     if (m_fake_mode_change && m_fullscreen) {
650         try {
651             m_framebuffer.reset(new Framebuffer(Pt(m_app_width, m_app_height)));
652         } catch (const FramebufferFailedException& ex) {
653             std::cerr << "Fake resolution change failed. Reason: \"" << ex.what() << "\". Reverting to real resolution change." << std::endl;
654             m_fake_mode_change = false;
655         }
656     }
657 }
658 
Enter2DMode()659 void SDLGUI::Enter2DMode()
660 { Enter2DModeImpl(Value(AppWidth()), Value(AppHeight())); }
661 
Exit2DMode()662 void SDLGUI::Exit2DMode() {
663     glMatrixMode(GL_MODELVIEW);
664     glPopMatrix();
665 
666     glMatrixMode(GL_PROJECTION);
667     glPopMatrix();
668 
669     glPopAttrib();
670 }
671 
FramebuffersAvailable() const672 bool SDLGUI::FramebuffersAvailable() const
673 { return GLEW_EXT_framebuffer_object && GLEW_EXT_packed_depth_stencil; }
674