2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file sdl2_v.cpp Implementation of the SDL2 video driver. */
12 #include "../stdafx.h"
13 #include "../openttd.h"
14 #include "../gfx_func.h"
16 #include "../blitter/factory.hpp"
17 #include "../network/network.h"
18 #include "../thread.h"
19 #include "../progress.h"
20 #include "../core/random_func.hpp"
21 #include "../core/math_func.hpp"
22 #include "../fileio_func.h"
23 #include "../framerate_type.h"
24 #include "../window_func.h"
28 #include <condition_variable>
31 #include "../safeguards.h"
33 static FVideoDriver_SDL iFVideoDriver_SDL
;
35 static SDL_Window
*_sdl_window
;
36 static SDL_Surface
*_sdl_surface
;
37 static SDL_Surface
*_sdl_realscreen
;
39 /** Whether the drawing is/may be done in a separate thread. */
40 static bool _draw_threaded
;
41 /** Mutex to keep the access to the shared memory controlled. */
42 static std::recursive_mutex
*_draw_mutex
= nullptr;
43 /** Signal to draw the next frame. */
44 static std::condition_variable_any
*_draw_signal
= nullptr;
45 /** Should we keep continue drawing? */
46 static volatile bool _draw_continue
;
47 static Palette _local_palette
;
48 static SDL_Palette
*_sdl_palette
;
50 #define MAX_DIRTY_RECTS 100
51 static SDL_Rect _dirty_rects
[MAX_DIRTY_RECTS
];
52 static int _num_dirty_rects
;
55 static int _window_size_w
;
56 static int _window_size_h
;
58 void VideoDriver_SDL::MakeDirty(int left
, int top
, int width
, int height
)
60 if (_num_dirty_rects
< MAX_DIRTY_RECTS
) {
61 _dirty_rects
[_num_dirty_rects
].x
= left
;
62 _dirty_rects
[_num_dirty_rects
].y
= top
;
63 _dirty_rects
[_num_dirty_rects
].w
= width
;
64 _dirty_rects
[_num_dirty_rects
].h
= height
;
69 static void UpdatePalette(bool init
= false)
73 for (int i
= 0; i
!= _local_palette
.count_dirty
; i
++) {
74 pal
[i
].r
= _local_palette
.palette
[_local_palette
.first_dirty
+ i
].r
;
75 pal
[i
].g
= _local_palette
.palette
[_local_palette
.first_dirty
+ i
].g
;
76 pal
[i
].b
= _local_palette
.palette
[_local_palette
.first_dirty
+ i
].b
;
80 SDL_SetPaletteColors(_sdl_palette
, pal
, _local_palette
.first_dirty
, _local_palette
.count_dirty
);
81 SDL_SetSurfacePalette(_sdl_surface
, _sdl_palette
);
83 if (_sdl_surface
!= _sdl_realscreen
&& init
) {
84 /* When using a shadow surface, also set our palette on the real screen. This lets SDL
85 * allocate as many colors (or approximations) as
86 * possible, instead of using only the default SDL
87 * palette. This allows us to get more colors exactly
88 * right and might allow using better approximations for
91 * Note that colors allocations are tried in-order, so
92 * this favors colors further up into the palette. Also
93 * note that if two colors from the same animation
94 * sequence are approximated using the same color, that
95 * animation will stop working.
97 * Since changing the system palette causes the colours
98 * to change right away, and allocations might
99 * drastically change, we can't use this for animation,
100 * since that could cause weird coloring between the
101 * palette change and the blitting below, so we only set
102 * the real palette during initialisation.
104 SDL_SetSurfacePalette(_sdl_realscreen
, _sdl_palette
);
107 if (_sdl_surface
!= _sdl_realscreen
&& !init
) {
108 /* We're not using real hardware palette, but are letting SDL
109 * approximate the palette during shadow -> screen copy. To
110 * change the palette, we need to recopy the entire screen.
112 * Note that this operation can slow down the rendering
113 * considerably, especially since changing the shadow
114 * palette will need the next blit to re-detect the
115 * best mapping of shadow palette colors to real palette
116 * colors from scratch.
118 SDL_BlitSurface(_sdl_surface
, nullptr, _sdl_realscreen
, nullptr);
119 SDL_UpdateWindowSurface(_sdl_window
);
123 static void InitPalette()
125 _local_palette
= _cur_palette
;
126 _local_palette
.first_dirty
= 0;
127 _local_palette
.count_dirty
= 256;
131 static void CheckPaletteAnim()
133 if (_cur_palette
.count_dirty
!= 0) {
134 Blitter
*blitter
= BlitterFactory::GetCurrentBlitter();
136 switch (blitter
->UsePaletteAnimation()) {
137 case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND
:
141 case Blitter::PALETTE_ANIMATION_BLITTER
:
142 blitter
->PaletteAnimate(_local_palette
);
145 case Blitter::PALETTE_ANIMATION_NONE
:
151 _cur_palette
.count_dirty
= 0;
155 static void DrawSurfaceToScreen()
157 PerformanceMeasurer
framerate(PFE_VIDEO
);
159 int n
= _num_dirty_rects
;
162 _num_dirty_rects
= 0;
164 if (n
> MAX_DIRTY_RECTS
) {
165 if (_sdl_surface
!= _sdl_realscreen
) {
166 SDL_BlitSurface(_sdl_surface
, nullptr, _sdl_realscreen
, nullptr);
169 SDL_UpdateWindowSurface(_sdl_window
);
171 if (_sdl_surface
!= _sdl_realscreen
) {
172 for (int i
= 0; i
< n
; i
++) {
174 _sdl_surface
, &_dirty_rects
[i
],
175 _sdl_realscreen
, &_dirty_rects
[i
]);
179 SDL_UpdateWindowSurfaceRects(_sdl_window
, _dirty_rects
, n
);
183 static void DrawSurfaceToScreenThread()
185 /* First tell the main thread we're started */
186 std::unique_lock
<std::recursive_mutex
> lock(*_draw_mutex
);
187 _draw_signal
->notify_one();
189 /* Now wait for the first thing to draw! */
190 _draw_signal
->wait(*_draw_mutex
);
192 while (_draw_continue
) {
194 /* Then just draw and wait till we stop */
195 DrawSurfaceToScreen();
196 _draw_signal
->wait(lock
);
200 static void GetVideoModes()
202 int modes
= SDL_GetNumDisplayModes(0);
203 if (modes
== 0) usererror("sdl: no modes available");
205 _resolutions
.clear();
207 SDL_DisplayMode mode
;
208 for (int i
= 0; i
< modes
; i
++) {
209 SDL_GetDisplayMode(0, i
, &mode
);
214 if (w
< 640 || h
< 480) continue; // reject too small resolutions
216 if (std::find(_resolutions
.begin(), _resolutions
.end(), Dimension(w
, h
)) != _resolutions
.end()) continue;
217 _resolutions
.emplace_back(w
, h
);
219 if (_resolutions
.empty()) usererror("No usable screen resolutions found!\n");
223 static void GetAvailableVideoMode(uint
*w
, uint
*h
)
225 /* All modes available? */
226 if (!_fullscreen
|| _resolutions
.empty()) return;
228 /* Is the wanted mode among the available modes? */
229 if (std::find(_resolutions
.begin(), _resolutions
.end(), Dimension(*w
, *h
)) != _resolutions
.end()) return;
231 /* Use the closest possible resolution */
233 uint delta
= Delta(_resolutions
[0].width
, *w
) * Delta(_resolutions
[0].height
, *h
);
234 for (uint i
= 1; i
!= _resolutions
.size(); ++i
) {
235 uint newdelta
= Delta(_resolutions
[i
].width
, *w
) * Delta(_resolutions
[i
].height
, *h
);
236 if (newdelta
< delta
) {
241 *w
= _resolutions
[best
].width
;
242 *h
= _resolutions
[best
].height
;
245 bool VideoDriver_SDL::CreateMainSurface(uint w
, uint h
, bool resize
)
247 SDL_Surface
*newscreen
;
249 int bpp
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
251 GetAvailableVideoMode(&w
, &h
);
253 DEBUG(driver
, 1, "SDL2: using mode %ux%ux%d", w
, h
, bpp
);
255 if (bpp
== 0) usererror("Can't use a blitter that blits 0 bpp for normal visuals");
257 /* Free any previously allocated shadow surface */
258 if (_sdl_surface
!= nullptr && _sdl_surface
!= _sdl_realscreen
) SDL_FreeSurface(_sdl_surface
);
260 seprintf(caption
, lastof(caption
), "OpenTTD %s", _openttd_revision
);
262 if (_sdl_window
== nullptr) {
263 Uint32 flags
= SDL_WINDOW_SHOWN
;
266 flags
|= SDL_WINDOW_FULLSCREEN
;
268 flags
|= SDL_WINDOW_RESIZABLE
;
271 _sdl_window
= SDL_CreateWindow(
273 SDL_WINDOWPOS_UNDEFINED
,
274 SDL_WINDOWPOS_UNDEFINED
,
278 if (_sdl_window
== nullptr) {
279 DEBUG(driver
, 0, "SDL2: Couldn't allocate a window to draw on");
283 char icon_path
[MAX_PATH
];
284 if (FioFindFullPath(icon_path
, lastof(icon_path
), BASESET_DIR
, "openttd.32.bmp") != nullptr) {
285 /* Give the application an icon */
286 SDL_Surface
*icon
= SDL_LoadBMP(icon_path
);
287 if (icon
!= nullptr) {
288 /* Get the colourkey, which will be magenta */
289 uint32 rgbmap
= SDL_MapRGB(icon
->format
, 255, 0, 255);
291 SDL_SetColorKey(icon
, SDL_TRUE
, rgbmap
);
292 SDL_SetWindowIcon(_sdl_window
, icon
);
293 SDL_FreeSurface(icon
);
298 if (resize
) SDL_SetWindowSize(_sdl_window
, w
, h
);
300 newscreen
= SDL_GetWindowSurface(_sdl_window
);
301 if (newscreen
== NULL
) {
302 DEBUG(driver
, 0, "SDL2: Couldn't get window surface: %s", SDL_GetError());
306 _sdl_realscreen
= newscreen
;
309 newscreen
= SDL_CreateRGBSurface(0, w
, h
, 8, 0, 0, 0, 0);
311 if (newscreen
== nullptr) {
312 DEBUG(driver
, 0, "SDL2: Couldn't allocate shadow surface: %s", SDL_GetError());
317 if (_sdl_palette
== nullptr) {
318 _sdl_palette
= SDL_AllocPalette(256);
321 if (_sdl_palette
== nullptr) {
322 DEBUG(driver
, 0, "SDL_AllocPalette() failed: %s", SDL_GetError());
326 /* Delay drawing for this cycle; the next cycle will redraw the whole screen */
327 _num_dirty_rects
= 0;
329 _screen
.width
= newscreen
->w
;
330 _screen
.height
= newscreen
->h
;
331 _screen
.pitch
= newscreen
->pitch
/ (bpp
/ 8);
332 _screen
.dst_ptr
= newscreen
->pixels
;
333 _sdl_surface
= newscreen
;
335 /* When in full screen, we will always have the mouse cursor
336 * within the window, even though SDL does not give us the
337 * appropriate event to know this. */
338 if (_fullscreen
) _cursor
.in_window
= true;
340 Blitter
*blitter
= BlitterFactory::GetCurrentBlitter();
341 blitter
->PostResize();
350 bool VideoDriver_SDL::ClaimMousePointer()
357 * This is called to indicate that an edit box has gained focus, text input mode should be enabled.
359 void VideoDriver_SDL::EditBoxGainedFocus()
361 if (!this->edit_box_focused
) {
362 SDL_StartTextInput();
363 this->edit_box_focused
= true;
368 * This is called to indicate that an edit box has lost focus, text input mode should be disabled.
370 void VideoDriver_SDL::EditBoxLostFocus()
372 if (this->edit_box_focused
) {
374 this->edit_box_focused
= false;
386 #define AS(x, z) {x, 0, z, false}
387 #define AM(x, y, z, w) {x, (byte)(y - x), z, false}
388 #define AS_UP(x, z) {x, 0, z, true}
389 #define AM_UP(x, y, z, w) {x, (byte)(y - x), z, true}
391 static const VkMapping _vk_mapping
[] = {
392 /* Pageup stuff + up/down */
393 AS_UP(SDLK_PAGEUP
, WKC_PAGEUP
),
394 AS_UP(SDLK_PAGEDOWN
, WKC_PAGEDOWN
),
395 AS_UP(SDLK_UP
, WKC_UP
),
396 AS_UP(SDLK_DOWN
, WKC_DOWN
),
397 AS_UP(SDLK_LEFT
, WKC_LEFT
),
398 AS_UP(SDLK_RIGHT
, WKC_RIGHT
),
400 AS_UP(SDLK_HOME
, WKC_HOME
),
401 AS_UP(SDLK_END
, WKC_END
),
403 AS_UP(SDLK_INSERT
, WKC_INSERT
),
404 AS_UP(SDLK_DELETE
, WKC_DELETE
),
406 /* Map letters & digits */
407 AM(SDLK_a
, SDLK_z
, 'A', 'Z'),
408 AM(SDLK_0
, SDLK_9
, '0', '9'),
410 AS_UP(SDLK_ESCAPE
, WKC_ESC
),
411 AS_UP(SDLK_PAUSE
, WKC_PAUSE
),
412 AS_UP(SDLK_BACKSPACE
, WKC_BACKSPACE
),
414 AS(SDLK_SPACE
, WKC_SPACE
),
415 AS(SDLK_RETURN
, WKC_RETURN
),
416 AS(SDLK_TAB
, WKC_TAB
),
419 AM_UP(SDLK_F1
, SDLK_F12
, WKC_F1
, WKC_F12
),
422 AM(SDLK_KP_0
, SDLK_KP_9
, '0', '9'),
423 AS(SDLK_KP_DIVIDE
, WKC_NUM_DIV
),
424 AS(SDLK_KP_MULTIPLY
, WKC_NUM_MUL
),
425 AS(SDLK_KP_MINUS
, WKC_NUM_MINUS
),
426 AS(SDLK_KP_PLUS
, WKC_NUM_PLUS
),
427 AS(SDLK_KP_ENTER
, WKC_NUM_ENTER
),
428 AS(SDLK_KP_PERIOD
, WKC_NUM_DECIMAL
),
430 /* Other non-letter keys */
431 AS(SDLK_SLASH
, WKC_SLASH
),
432 AS(SDLK_SEMICOLON
, WKC_SEMICOLON
),
433 AS(SDLK_EQUALS
, WKC_EQUALS
),
434 AS(SDLK_LEFTBRACKET
, WKC_L_BRACKET
),
435 AS(SDLK_BACKSLASH
, WKC_BACKSLASH
),
436 AS(SDLK_RIGHTBRACKET
, WKC_R_BRACKET
),
438 AS(SDLK_QUOTE
, WKC_SINGLEQUOTE
),
439 AS(SDLK_COMMA
, WKC_COMMA
),
440 AS(SDLK_MINUS
, WKC_MINUS
),
441 AS(SDLK_PERIOD
, WKC_PERIOD
)
444 static uint
ConvertSdlKeyIntoMy(SDL_Keysym
*sym
, WChar
*character
)
446 const VkMapping
*map
;
448 bool unprintable
= false;
450 for (map
= _vk_mapping
; map
!= endof(_vk_mapping
); ++map
) {
451 if ((uint
)(sym
->sym
- map
->vk_from
) <= map
->vk_count
) {
452 key
= sym
->sym
- map
->vk_from
+ map
->map_to
;
453 unprintable
= map
->unprintable
;
458 /* check scancode for BACKQUOTE key, because we want the key left of "1", not anything else (on non-US keyboards) */
459 if (sym
->scancode
== SDL_SCANCODE_GRAVE
) key
= WKC_BACKQUOTE
;
461 /* META are the command keys on mac */
462 if (sym
->mod
& KMOD_GUI
) key
|= WKC_META
;
463 if (sym
->mod
& KMOD_SHIFT
) key
|= WKC_SHIFT
;
464 if (sym
->mod
& KMOD_CTRL
) key
|= WKC_CTRL
;
465 if (sym
->mod
& KMOD_ALT
) key
|= WKC_ALT
;
467 /* The mod keys have no character. Prevent '?' */
468 if (sym
->mod
& KMOD_GUI
||
469 sym
->mod
& KMOD_CTRL
||
470 sym
->mod
& KMOD_ALT
||
472 *character
= WKC_NONE
;
474 *character
= sym
->sym
;
481 * Like ConvertSdlKeyIntoMy(), but takes an SDL_Keycode as input
482 * instead of an SDL_Keysym.
484 static uint
ConvertSdlKeycodeIntoMy(SDL_Keycode kc
)
486 const VkMapping
*map
;
489 for (map
= _vk_mapping
; map
!= endof(_vk_mapping
); ++map
) {
490 if ((uint
)(kc
- map
->vk_from
) <= map
->vk_count
) {
491 key
= kc
- map
->vk_from
+ map
->map_to
;
496 /* check scancode for BACKQUOTE key, because we want the key left
497 of "1", not anything else (on non-US keyboards) */
498 SDL_Scancode sc
= SDL_GetScancodeFromKey(kc
);
499 if (sc
== SDL_SCANCODE_GRAVE
) key
= WKC_BACKQUOTE
;
504 int VideoDriver_SDL::PollEvent()
508 if (!SDL_PollEvent(&ev
)) return -2;
511 case SDL_MOUSEMOTION
:
512 if (_cursor
.UpdateCursorPosition(ev
.motion
.x
, ev
.motion
.y
, true)) {
513 SDL_WarpMouseInWindow(_sdl_window
, _cursor
.pos
.x
, _cursor
.pos
.y
);
519 if (ev
.wheel
.y
> 0) {
521 } else if (ev
.wheel
.y
< 0) {
526 case SDL_MOUSEBUTTONDOWN
:
527 if (_rightclick_emulate
&& SDL_GetModState() & KMOD_CTRL
) {
528 ev
.button
.button
= SDL_BUTTON_RIGHT
;
531 switch (ev
.button
.button
) {
532 case SDL_BUTTON_LEFT
:
533 _left_button_down
= true;
536 case SDL_BUTTON_RIGHT
:
537 _right_button_down
= true;
538 _right_button_clicked
= true;
546 case SDL_MOUSEBUTTONUP
:
547 if (_rightclick_emulate
) {
548 _right_button_down
= false;
549 _left_button_down
= false;
550 _left_button_clicked
= false;
551 } else if (ev
.button
.button
== SDL_BUTTON_LEFT
) {
552 _left_button_down
= false;
553 _left_button_clicked
= false;
554 } else if (ev
.button
.button
== SDL_BUTTON_RIGHT
) {
555 _right_button_down
= false;
561 HandleExitGameRequest();
564 case SDL_KEYDOWN
: // Toggle full-screen on ALT + ENTER/F
565 if ((ev
.key
.keysym
.mod
& (KMOD_ALT
| KMOD_GUI
)) &&
566 (ev
.key
.keysym
.sym
== SDLK_RETURN
|| ev
.key
.keysym
.sym
== SDLK_f
)) {
567 if (ev
.key
.repeat
== 0) ToggleFullScreen(!_fullscreen
);
571 uint keycode
= ConvertSdlKeyIntoMy(&ev
.key
.keysym
, &character
);
572 // Only handle non-text keys here. Text is handled in
573 // SDL_TEXTINPUT below.
574 if (!this->edit_box_focused
||
575 keycode
== WKC_DELETE
||
576 keycode
== WKC_NUM_ENTER
||
577 keycode
== WKC_LEFT
||
578 keycode
== WKC_RIGHT
||
580 keycode
== WKC_DOWN
||
581 keycode
== WKC_HOME
||
582 keycode
== WKC_END
||
583 keycode
& WKC_META
||
584 keycode
& WKC_CTRL
||
586 (keycode
>= WKC_F1
&& keycode
<= WKC_F12
) ||
587 !IsValidChar(character
, CS_ALPHANUMERAL
)) {
588 HandleKeypress(keycode
, character
);
593 case SDL_TEXTINPUT
: {
594 if (!this->edit_box_focused
) break;
595 SDL_Keycode kc
= SDL_GetKeyFromName(ev
.text
.text
);
596 uint keycode
= ConvertSdlKeycodeIntoMy(kc
);
598 if (keycode
== WKC_BACKQUOTE
&& FocusedWindowIsConsole()) {
600 Utf8Decode(&character
, ev
.text
.text
);
601 HandleKeypress(keycode
, character
);
603 HandleTextInput(ev
.text
.text
);
607 case SDL_WINDOWEVENT
: {
608 if (ev
.window
.event
== SDL_WINDOWEVENT_EXPOSED
) {
609 // Force a redraw of the entire screen.
610 _num_dirty_rects
= MAX_DIRTY_RECTS
+ 1;
611 } else if (ev
.window
.event
== SDL_WINDOWEVENT_SIZE_CHANGED
) {
612 int w
= max(ev
.window
.data1
, 64);
613 int h
= max(ev
.window
.data2
, 64);
614 CreateMainSurface(w
, h
, w
!= ev
.window
.data1
|| h
!= ev
.window
.data2
);
615 } else if (ev
.window
.event
== SDL_WINDOWEVENT_ENTER
) {
616 // mouse entered the window, enable cursor
617 _cursor
.in_window
= true;
618 } else if (ev
.window
.event
== SDL_WINDOWEVENT_LEAVE
) {
619 // mouse left the window, undraw cursor
621 _cursor
.in_window
= false;
629 const char *VideoDriver_SDL::Start(const char * const *parm
)
631 /* Explicitly disable hardware acceleration. Enabling this causes
632 * UpdateWindowSurface() to update the window's texture instead of
634 SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION
, "0");
636 /* Just on the offchance the audio subsystem started before the video system,
637 * check whether any part of SDL has been initialised before getting here.
638 * Slightly duplicated with sound/sdl_s.cpp */
640 if (SDL_WasInit(SDL_INIT_VIDEO
) == 0) {
641 ret_code
= SDL_InitSubSystem(SDL_INIT_VIDEO
);
643 if (ret_code
< 0) return SDL_GetError();
646 if (!CreateMainSurface(_cur_resolution
.width
, _cur_resolution
.height
, false)) {
647 return SDL_GetError();
650 const char *dname
= SDL_GetCurrentVideoDriver();
651 DEBUG(driver
, 1, "SDL2: using driver '%s'", dname
);
653 MarkWholeScreenDirty();
655 _draw_threaded
= GetDriverParam(parm
, "no_threads") == nullptr && GetDriverParam(parm
, "no_thread") == nullptr;
658 this->edit_box_focused
= false;
663 void VideoDriver_SDL::Stop()
665 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
666 if (SDL_WasInit(SDL_INIT_EVERYTHING
) == 0) {
667 SDL_Quit(); // If there's nothing left, quit SDL
671 void VideoDriver_SDL::MainLoop()
673 uint32 cur_ticks
= SDL_GetTicks();
674 uint32 last_cur_ticks
= cur_ticks
;
675 uint32 next_tick
= cur_ticks
+ MILLISECONDS_PER_TICK
;
682 std::thread draw_thread
;
683 std::unique_lock
<std::recursive_mutex
> draw_lock
;
684 if (_draw_threaded
) {
685 /* Initialise the mutex first, because that's the thing we *need*
686 * directly in the newly created thread. */
687 _draw_mutex
= new std::recursive_mutex();
688 if (_draw_mutex
== nullptr) {
689 _draw_threaded
= false;
691 draw_lock
= std::unique_lock
<std::recursive_mutex
>(*_draw_mutex
);
692 _draw_signal
= new std::condition_variable_any();
693 _draw_continue
= true;
695 _draw_threaded
= StartNewThread(&draw_thread
, "ottd:draw-sdl", &DrawSurfaceToScreenThread
);
697 /* Free the mutex if we won't be able to use it. */
698 if (!_draw_threaded
) {
703 _draw_mutex
= nullptr;
704 _draw_signal
= nullptr;
706 /* Wait till the draw mutex has started itself. */
707 _draw_signal
->wait(*_draw_mutex
);
712 DEBUG(driver
, 1, "SDL2: using %sthreads", _draw_threaded
? "" : "no ");
715 uint32 prev_cur_ticks
= cur_ticks
; // to check for wrapping
716 InteractiveRandom(); // randomness
718 while (PollEvent() == -1) {}
719 if (_exit_game
) break;
721 mod
= SDL_GetModState();
722 keys
= SDL_GetKeyboardState(&numkeys
);
727 /* Speedup when pressing tab, except when using ALT+TAB
728 * to switch to another application */
729 if (keys
[SDL_SCANCODE_TAB
] && (mod
& KMOD_ALT
) == 0)
730 #endif /* defined(_DEBUG) */
732 if (!_networking
&& _game_mode
!= GM_MENU
) _fast_forward
|= 2;
733 } else if (_fast_forward
& 2) {
737 cur_ticks
= SDL_GetTicks();
738 if (SDL_TICKS_PASSED(cur_ticks
, next_tick
) || (_fast_forward
&& !_pause_mode
) || cur_ticks
< prev_cur_ticks
) {
739 _realtime_tick
+= cur_ticks
- last_cur_ticks
;
740 last_cur_ticks
= cur_ticks
;
741 next_tick
= cur_ticks
+ MILLISECONDS_PER_TICK
;
743 bool old_ctrl_pressed
= _ctrl_pressed
;
745 _ctrl_pressed
= !!(mod
& KMOD_CTRL
);
746 _shift_pressed
= !!(mod
& KMOD_SHIFT
);
748 /* determine which directional keys are down */
750 (keys
[SDL_SCANCODE_LEFT
] ? 1 : 0) |
751 (keys
[SDL_SCANCODE_UP
] ? 2 : 0) |
752 (keys
[SDL_SCANCODE_RIGHT
] ? 4 : 0) |
753 (keys
[SDL_SCANCODE_DOWN
] ? 8 : 0);
754 if (old_ctrl_pressed
!= _ctrl_pressed
) HandleCtrlChanged();
756 /* The gameloop is the part that can run asynchronously. The rest
757 * except sleeping can't. */
758 if (_draw_mutex
!= nullptr) draw_lock
.unlock();
762 if (_draw_mutex
!= nullptr) draw_lock
.lock();
765 _local_palette
= _cur_palette
;
767 /* Release the thread while sleeping */
768 if (_draw_mutex
!= nullptr) draw_lock
.unlock();
770 if (_draw_mutex
!= nullptr) draw_lock
.lock();
772 NetworkDrawChatMessage();
776 /* End of the critical part. */
777 if (_draw_mutex
!= nullptr && !HasModalProgress()) {
778 _draw_signal
->notify_one();
780 /* Oh, we didn't have threads, then just draw unthreaded */
782 DrawSurfaceToScreen();
786 if (_draw_mutex
!= nullptr) {
787 _draw_continue
= false;
788 /* Sending signal if there is no thread blocked
789 * is very valid and results in noop */
790 _draw_signal
->notify_one();
791 if (draw_lock
.owns_lock()) draw_lock
.unlock();
798 _draw_mutex
= nullptr;
799 _draw_signal
= nullptr;
803 bool VideoDriver_SDL::ChangeResolution(int w
, int h
)
805 std::unique_lock
<std::recursive_mutex
> lock
;
806 if (_draw_mutex
!= nullptr) lock
= std::unique_lock
<std::recursive_mutex
>(*_draw_mutex
);
808 return CreateMainSurface(w
, h
, true);
811 bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen
)
813 std::unique_lock
<std::recursive_mutex
> lock
;
814 if (_draw_mutex
!= nullptr) lock
= std::unique_lock
<std::recursive_mutex
>(*_draw_mutex
);
816 /* Remember current window size */
818 SDL_GetWindowSize(_sdl_window
, &_window_size_w
, &_window_size_h
);
820 /* Find fullscreen window size */
822 if (SDL_GetCurrentDisplayMode(0, &dm
) < 0) {
823 DEBUG(driver
, 0, "SDL_GetCurrentDisplayMode() failed: %s", SDL_GetError());
825 SDL_SetWindowSize(_sdl_window
, dm
.w
, dm
.h
);
829 DEBUG(driver
, 1, "SDL2: Setting %s", fullscreen
? "fullscreen" : "windowed");
830 int ret
= SDL_SetWindowFullscreen(_sdl_window
, fullscreen
? SDL_WINDOW_FULLSCREEN
: 0);
832 /* Switching resolution succeeded, set fullscreen value of window. */
833 _fullscreen
= fullscreen
;
834 if (!fullscreen
) SDL_SetWindowSize(_sdl_window
, _window_size_w
, _window_size_h
);
836 DEBUG(driver
, 0, "SDL_SetWindowFullscreen() failed: %s", SDL_GetError());
842 bool VideoDriver_SDL::AfterBlitterChange()
845 SDL_GetWindowSize(_sdl_window
, &w
, &h
);
846 return CreateMainSurface(w
, h
, false);
849 void VideoDriver_SDL::AcquireBlitterLock()
851 if (_draw_mutex
!= nullptr) _draw_mutex
->lock();
854 void VideoDriver_SDL::ReleaseBlitterLock()
856 if (_draw_mutex
!= nullptr) _draw_mutex
->unlock();
859 #endif /* WITH_SDL2 */