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