4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file sdl_v.cpp Implementation of the SDL video driver. */
14 #include "../stdafx.h"
15 #include "../openttd.h"
16 #include "../gfx_func.h"
19 #include "../blitter/factory.hpp"
20 #include "../network/network.h"
21 #include "../thread/thread.h"
22 #include "../progress.h"
23 #include "../core/random_func.hpp"
24 #include "../core/math_func.hpp"
25 #include "../fileio_func.h"
29 #include "../safeguards.h"
31 static FVideoDriver_SDL iFVideoDriver_SDL
;
33 static SDL_Surface
*_sdl_screen
;
34 static SDL_Surface
*_sdl_realscreen
;
35 static bool _all_modes
;
37 /** Whether the drawing is/may be done in a separate thread. */
38 static bool _draw_threaded
;
39 /** Thread used to 'draw' to the screen, i.e. push data to the screen. */
40 static ThreadObject
*_draw_thread
= NULL
;
41 /** Mutex to keep the access to the shared memory controlled. */
42 static ThreadMutex
*_draw_mutex
= NULL
;
43 /** Should we keep continue drawing? */
44 static volatile bool _draw_continue
;
45 static Palette _local_palette
;
47 #define MAX_DIRTY_RECTS 100
48 static SDL_Rect _dirty_rects
[MAX_DIRTY_RECTS
];
49 static int _num_dirty_rects
;
50 static int _use_hwpalette
;
51 static int _requested_hwpalette
; /* Did we request a HWPALETTE for the current video mode? */
53 void VideoDriver_SDL::MakeDirty(int left
, int top
, int width
, int height
)
55 if (_num_dirty_rects
< MAX_DIRTY_RECTS
) {
56 _dirty_rects
[_num_dirty_rects
].x
= left
;
57 _dirty_rects
[_num_dirty_rects
].y
= top
;
58 _dirty_rects
[_num_dirty_rects
].w
= width
;
59 _dirty_rects
[_num_dirty_rects
].h
= height
;
64 static void UpdatePalette(bool init
= false)
68 for (int i
= 0; i
!= _local_palette
.count_dirty
; i
++) {
69 pal
[i
].r
= _local_palette
.palette
[_local_palette
.first_dirty
+ i
].r
;
70 pal
[i
].g
= _local_palette
.palette
[_local_palette
.first_dirty
+ i
].g
;
71 pal
[i
].b
= _local_palette
.palette
[_local_palette
.first_dirty
+ i
].b
;
75 SDL_CALL
SDL_SetColors(_sdl_screen
, pal
, _local_palette
.first_dirty
, _local_palette
.count_dirty
);
77 if (_sdl_screen
!= _sdl_realscreen
&& init
) {
78 /* When using a shadow surface, also set our palette on the real screen. This lets SDL
79 * allocate as much colors (or approximations) as
80 * possible, instead of using only the default SDL
81 * palette. This allows us to get more colors exactly
82 * right and might allow using better approximations for
85 * Note that colors allocations are tried in-order, so
86 * this favors colors further up into the palette. Also
87 * note that if two colors from the same animation
88 * sequence are approximated using the same color, that
89 * animation will stop working.
91 * Since changing the system palette causes the colours
92 * to change right away, and allocations might
93 * drastically change, we can't use this for animation,
94 * since that could cause weird coloring between the
95 * palette change and the blitting below, so we only set
96 * the real palette during initialisation.
98 SDL_CALL
SDL_SetColors(_sdl_realscreen
, pal
, _local_palette
.first_dirty
, _local_palette
.count_dirty
);
101 if (_sdl_screen
!= _sdl_realscreen
&& !init
) {
102 /* We're not using real hardware palette, but are letting SDL
103 * approximate the palette during shadow -> screen copy. To
104 * change the palette, we need to recopy the entire screen.
106 * Note that this operation can slow down the rendering
107 * considerably, especially since changing the shadow
108 * palette will need the next blit to re-detect the
109 * best mapping of shadow palette colors to real palette
110 * colors from scratch.
112 SDL_CALL
SDL_BlitSurface(_sdl_screen
, NULL
, _sdl_realscreen
, NULL
);
113 SDL_CALL
SDL_UpdateRect(_sdl_realscreen
, 0, 0, 0, 0);
117 static void InitPalette()
119 _local_palette
= _cur_palette
;
120 _local_palette
.first_dirty
= 0;
121 _local_palette
.count_dirty
= 256;
125 static void CheckPaletteAnim()
127 if (_cur_palette
.count_dirty
!= 0) {
128 Blitter
*blitter
= BlitterFactory::GetCurrentBlitter();
130 switch (blitter
->UsePaletteAnimation()) {
131 case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND
:
135 case Blitter::PALETTE_ANIMATION_BLITTER
:
136 blitter
->PaletteAnimate(_local_palette
);
139 case Blitter::PALETTE_ANIMATION_NONE
:
145 _cur_palette
.count_dirty
= 0;
149 static void DrawSurfaceToScreen()
151 int n
= _num_dirty_rects
;
154 _num_dirty_rects
= 0;
155 if (n
> MAX_DIRTY_RECTS
) {
156 if (_sdl_screen
!= _sdl_realscreen
) {
157 SDL_CALL
SDL_BlitSurface(_sdl_screen
, NULL
, _sdl_realscreen
, NULL
);
159 SDL_CALL
SDL_UpdateRect(_sdl_realscreen
, 0, 0, 0, 0);
161 if (_sdl_screen
!= _sdl_realscreen
) {
162 for (int i
= 0; i
< n
; i
++) {
163 SDL_CALL
SDL_BlitSurface(_sdl_screen
, &_dirty_rects
[i
], _sdl_realscreen
, &_dirty_rects
[i
]);
166 SDL_CALL
SDL_UpdateRects(_sdl_realscreen
, n
, _dirty_rects
);
170 static void DrawSurfaceToScreenThread(void *)
172 /* First tell the main thread we're started */
173 _draw_mutex
->BeginCritical();
174 _draw_mutex
->SendSignal();
176 /* Now wait for the first thing to draw! */
177 _draw_mutex
->WaitForSignal();
179 while (_draw_continue
) {
181 /* Then just draw and wait till we stop */
182 DrawSurfaceToScreen();
183 _draw_mutex
->WaitForSignal();
186 _draw_mutex
->EndCritical();
187 _draw_thread
->Exit();
190 static const Dimension _default_resolutions
[] = {
204 static void GetVideoModes()
206 SDL_Rect
**modes
= SDL_CALL
SDL_ListModes(NULL
, SDL_SWSURFACE
| SDL_FULLSCREEN
);
207 if (modes
== NULL
) usererror("sdl: no modes available");
209 _all_modes
= (SDL_CALL
SDL_ListModes(NULL
, SDL_SWSURFACE
| (_fullscreen
? SDL_FULLSCREEN
: 0)) == (void*)-1);
210 if (modes
== (void*)-1) {
212 for (uint i
= 0; i
< lengthof(_default_resolutions
); i
++) {
213 if (SDL_CALL
SDL_VideoModeOK(_default_resolutions
[i
].width
, _default_resolutions
[i
].height
, 8, SDL_FULLSCREEN
) != 0) {
214 _resolutions
[n
] = _default_resolutions
[i
];
215 if (++n
== lengthof(_resolutions
)) break;
218 _num_resolutions
= n
;
221 for (int i
= 0; modes
[i
]; i
++) {
222 uint w
= modes
[i
]->w
;
223 uint h
= modes
[i
]->h
;
225 for (j
= 0; j
< n
; j
++) {
226 if (_resolutions
[j
].width
== w
&& _resolutions
[j
].height
== h
) break;
230 _resolutions
[j
].width
= w
;
231 _resolutions
[j
].height
= h
;
232 if (++n
== lengthof(_resolutions
)) break;
235 _num_resolutions
= n
;
236 SortResolutions(_num_resolutions
);
240 static void GetAvailableVideoMode(uint
*w
, uint
*h
)
242 /* All modes available? */
243 if (_all_modes
|| _num_resolutions
== 0) return;
245 /* Is the wanted mode among the available modes? */
246 for (int i
= 0; i
!= _num_resolutions
; i
++) {
247 if (*w
== _resolutions
[i
].width
&& *h
== _resolutions
[i
].height
) return;
250 /* Use the closest possible resolution */
252 uint delta
= Delta(_resolutions
[0].width
, *w
) * Delta(_resolutions
[0].height
, *h
);
253 for (int i
= 1; i
!= _num_resolutions
; ++i
) {
254 uint newdelta
= Delta(_resolutions
[i
].width
, *w
) * Delta(_resolutions
[i
].height
, *h
);
255 if (newdelta
< delta
) {
260 *w
= _resolutions
[best
].width
;
261 *h
= _resolutions
[best
].height
;
265 /* Let's redefine the LoadBMP macro with because we are dynamically
266 * loading SDL and need to 'SDL_CALL' all functions */
268 #define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_CALL SDL_RWFromFile(file, "rb"), 1)
271 bool VideoDriver_SDL::CreateMainSurface(uint w
, uint h
)
273 SDL_Surface
*newscreen
, *icon
;
275 int bpp
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
278 GetAvailableVideoMode(&w
, &h
);
280 DEBUG(driver
, 1, "SDL: using mode %ux%ux%d", w
, h
, bpp
);
282 if (bpp
== 0) usererror("Can't use a blitter that blits 0 bpp for normal visuals");
284 char icon_path
[MAX_PATH
];
285 if (FioFindFullPath(icon_path
, lastof(icon_path
), BASESET_DIR
, "openttd.32.bmp") != NULL
) {
286 /* Give the application an icon */
287 icon
= SDL_CALL
SDL_LoadBMP(icon_path
);
289 /* Get the colourkey, which will be magenta */
290 uint32 rgbmap
= SDL_CALL
SDL_MapRGB(icon
->format
, 255, 0, 255);
292 SDL_CALL
SDL_SetColorKey(icon
, SDL_SRCCOLORKEY
, rgbmap
);
293 SDL_CALL
SDL_WM_SetIcon(icon
, NULL
);
294 SDL_CALL
SDL_FreeSurface(icon
);
298 if (_use_hwpalette
== 2) {
299 /* Default is to autodetect when to use SDL_HWPALETTE.
300 * In this case, SDL_HWPALETTE is only used for 8bpp
301 * blitters in fullscreen.
303 * When using an 8bpp blitter on a 8bpp system in
304 * windowed mode with SDL_HWPALETTE, OpenTTD will claim
305 * the system palette, making all other applications
306 * get the wrong colours. In this case, we're better of
307 * trying to approximate the colors we need using system
308 * colors, using a shadow surface (see below).
310 * On a 32bpp system, SDL_HWPALETTE is ignored, so it
311 * doesn't matter what we do.
313 * When using a 32bpp blitter on a 8bpp system, setting
314 * SDL_HWPALETTE messes up rendering (at least on X11),
315 * so we don't do that. In this case, SDL takes care of
316 * color approximation using its own shadow surface
317 * (which we can't force in 8bpp on 8bpp mode,
320 want_hwpalette
= bpp
== 8 && _fullscreen
&& _support8bpp
== S8BPP_HARDWARE
;
322 /* User specified a value manually */
323 want_hwpalette
= _use_hwpalette
;
326 if (want_hwpalette
) DEBUG(driver
, 1, "SDL: requesting hardware palete");
328 /* Free any previously allocated shadow surface */
329 if (_sdl_screen
!= NULL
&& _sdl_screen
!= _sdl_realscreen
) SDL_CALL
SDL_FreeSurface(_sdl_screen
);
331 if (_sdl_realscreen
!= NULL
) {
332 if (_requested_hwpalette
!= want_hwpalette
) {
333 /* SDL (at least the X11 driver), reuses the
334 * same window and palette settings when the bpp
335 * (and a few flags) are the same. Since we need
336 * to hwpalette value to change (in particular
337 * when switching between fullscreen and
338 * windowed), we restart the entire video
339 * subsystem to force creating a new window.
341 DEBUG(driver
, 0, "SDL: Restarting SDL video subsystem, to force hwpalette change");
342 SDL_CALL
SDL_QuitSubSystem(SDL_INIT_VIDEO
);
343 SDL_CALL
SDL_InitSubSystem(SDL_INIT_VIDEO
);
348 /* Remember if we wanted a hwpalette. We can't reliably query
349 * SDL for the SDL_HWPALETTE flag, since it might get set even
350 * though we didn't ask for it (when SDL creates a shadow
351 * surface, for example). */
352 _requested_hwpalette
= want_hwpalette
;
354 /* DO NOT CHANGE TO HWSURFACE, IT DOES NOT WORK */
355 newscreen
= SDL_CALL
SDL_SetVideoMode(w
, h
, bpp
, SDL_SWSURFACE
| (want_hwpalette
? SDL_HWPALETTE
: 0) | (_fullscreen
? SDL_FULLSCREEN
: SDL_RESIZABLE
));
356 if (newscreen
== NULL
) {
357 DEBUG(driver
, 0, "SDL: Couldn't allocate a window to draw on");
360 _sdl_realscreen
= newscreen
;
362 if (bpp
== 8 && (_sdl_realscreen
->flags
& SDL_HWPALETTE
) != SDL_HWPALETTE
) {
363 /* Using an 8bpp blitter, if we didn't get a hardware
364 * palette (most likely because we didn't request one,
365 * see above), we'll have to set up a shadow surface to
368 * Our palette will be applied to this shadow surface,
369 * while the real screen surface will use the shared
370 * system palette (which will partly contain our colors,
371 * but most likely will not have enough free color cells
372 * for all of our colors). SDL can use these two
373 * palettes at blit time to approximate colors used in
374 * the shadow surface using system colors automatically.
376 * Note that when using an 8bpp blitter on a 32bpp
377 * system, SDL will create an internal shadow surface.
378 * This shadow surface will have SDL_HWPALLETE set, so
379 * we won't create a second shadow surface in this case.
381 DEBUG(driver
, 1, "SDL: using shadow surface");
382 newscreen
= SDL_CALL
SDL_CreateRGBSurface(SDL_SWSURFACE
, w
, h
, bpp
, 0, 0, 0, 0);
383 if (newscreen
== NULL
) {
384 DEBUG(driver
, 0, "SDL: Couldn't allocate a shadow surface to draw on");
389 /* Delay drawing for this cycle; the next cycle will redraw the whole screen */
390 _num_dirty_rects
= 0;
392 _screen
.width
= newscreen
->w
;
393 _screen
.height
= newscreen
->h
;
394 _screen
.pitch
= newscreen
->pitch
/ (bpp
/ 8);
395 _screen
.dst_ptr
= newscreen
->pixels
;
396 _sdl_screen
= newscreen
;
398 /* When in full screen, we will always have the mouse cursor
399 * within the window, even though SDL does not give us the
400 * appropriate event to know this. */
401 if (_fullscreen
) _cursor
.in_window
= true;
403 Blitter
*blitter
= BlitterFactory::GetCurrentBlitter();
404 blitter
->PostResize();
408 seprintf(caption
, lastof(caption
), "OpenTTD %s", _openttd_revision
);
409 SDL_CALL
SDL_WM_SetCaption(caption
, caption
);
416 bool VideoDriver_SDL::ClaimMousePointer()
418 SDL_CALL
SDL_ShowCursor(0);
423 #if SDL_VERSION_ATLEAST(1, 3, 0)
432 #define AS(x, z) {x, 0, z}
433 #define AM(x, y, z, w) {x, (byte)(y - x), z}
435 static const VkMapping _vk_mapping
[] = {
436 /* Pageup stuff + up/down */
437 AM(SDLK_PAGEUP
, SDLK_PAGEDOWN
, WKC_PAGEUP
, WKC_PAGEDOWN
),
439 AS(SDLK_DOWN
, WKC_DOWN
),
440 AS(SDLK_LEFT
, WKC_LEFT
),
441 AS(SDLK_RIGHT
, WKC_RIGHT
),
443 AS(SDLK_HOME
, WKC_HOME
),
444 AS(SDLK_END
, WKC_END
),
446 AS(SDLK_INSERT
, WKC_INSERT
),
447 AS(SDLK_DELETE
, WKC_DELETE
),
449 /* Map letters & digits */
450 AM(SDLK_a
, SDLK_z
, 'A', 'Z'),
451 AM(SDLK_0
, SDLK_9
, '0', '9'),
453 AS(SDLK_ESCAPE
, WKC_ESC
),
454 AS(SDLK_PAUSE
, WKC_PAUSE
),
455 AS(SDLK_BACKSPACE
, WKC_BACKSPACE
),
457 AS(SDLK_SPACE
, WKC_SPACE
),
458 AS(SDLK_RETURN
, WKC_RETURN
),
459 AS(SDLK_TAB
, WKC_TAB
),
462 AM(SDLK_F1
, SDLK_F12
, WKC_F1
, WKC_F12
),
465 AM(SDLK_KP0
, SDLK_KP9
, '0', '9'),
466 AS(SDLK_KP_DIVIDE
, WKC_NUM_DIV
),
467 AS(SDLK_KP_MULTIPLY
, WKC_NUM_MUL
),
468 AS(SDLK_KP_MINUS
, WKC_NUM_MINUS
),
469 AS(SDLK_KP_PLUS
, WKC_NUM_PLUS
),
470 AS(SDLK_KP_ENTER
, WKC_NUM_ENTER
),
471 AS(SDLK_KP_PERIOD
, WKC_NUM_DECIMAL
),
473 /* Other non-letter keys */
474 AS(SDLK_SLASH
, WKC_SLASH
),
475 AS(SDLK_SEMICOLON
, WKC_SEMICOLON
),
476 AS(SDLK_EQUALS
, WKC_EQUALS
),
477 AS(SDLK_LEFTBRACKET
, WKC_L_BRACKET
),
478 AS(SDLK_BACKSLASH
, WKC_BACKSLASH
),
479 AS(SDLK_RIGHTBRACKET
, WKC_R_BRACKET
),
481 AS(SDLK_QUOTE
, WKC_SINGLEQUOTE
),
482 AS(SDLK_COMMA
, WKC_COMMA
),
483 AS(SDLK_MINUS
, WKC_MINUS
),
484 AS(SDLK_PERIOD
, WKC_PERIOD
)
487 static uint
ConvertSdlKeyIntoMy(SDL_keysym
*sym
, WChar
*character
)
489 const VkMapping
*map
;
492 for (map
= _vk_mapping
; map
!= endof(_vk_mapping
); ++map
) {
493 if ((uint
)(sym
->sym
- map
->vk_from
) <= map
->vk_count
) {
494 key
= sym
->sym
- map
->vk_from
+ map
->map_to
;
499 /* check scancode for BACKQUOTE key, because we want the key left of "1", not anything else (on non-US keyboards) */
500 #if defined(WIN32) || defined(__OS2__)
501 if (sym
->scancode
== 41) key
= WKC_BACKQUOTE
;
502 #elif defined(__APPLE__)
503 if (sym
->scancode
== 10) key
= WKC_BACKQUOTE
;
504 #elif defined(__MORPHOS__)
505 if (sym
->scancode
== 0) key
= WKC_BACKQUOTE
; // yes, that key is code '0' under MorphOS :)
506 #elif defined(__BEOS__)
507 if (sym
->scancode
== 17) key
= WKC_BACKQUOTE
;
508 #elif defined(__SVR4) && defined(__sun)
509 if (sym
->scancode
== 60) key
= WKC_BACKQUOTE
;
510 if (sym
->scancode
== 49) key
= WKC_BACKSPACE
;
511 #elif defined(__sgi__)
512 if (sym
->scancode
== 22) key
= WKC_BACKQUOTE
;
514 if (sym
->scancode
== 49) key
= WKC_BACKQUOTE
;
517 /* META are the command keys on mac */
518 if (sym
->mod
& KMOD_META
) key
|= WKC_META
;
519 if (sym
->mod
& KMOD_SHIFT
) key
|= WKC_SHIFT
;
520 if (sym
->mod
& KMOD_CTRL
) key
|= WKC_CTRL
;
521 if (sym
->mod
& KMOD_ALT
) key
|= WKC_ALT
;
523 *character
= sym
->unicode
;
527 int VideoDriver_SDL::PollEvent()
531 if (!SDL_CALL
SDL_PollEvent(&ev
)) return -2;
534 case SDL_MOUSEMOTION
:
535 if (_cursor
.UpdateCursorPosition(ev
.motion
.x
, ev
.motion
.y
, true)) {
536 SDL_CALL
SDL_WarpMouse(_cursor
.pos
.x
, _cursor
.pos
.y
);
541 case SDL_MOUSEBUTTONDOWN
:
542 if (_rightclick_emulate
&& SDL_CALL
SDL_GetModState() & KMOD_CTRL
) {
543 ev
.button
.button
= SDL_BUTTON_RIGHT
;
546 switch (ev
.button
.button
) {
547 case SDL_BUTTON_LEFT
:
548 _left_button_down
= true;
551 case SDL_BUTTON_RIGHT
:
552 _right_button_down
= true;
553 _right_button_clicked
= true;
556 case SDL_BUTTON_WHEELUP
: _cursor
.wheel
--; break;
557 case SDL_BUTTON_WHEELDOWN
: _cursor
.wheel
++; break;
564 case SDL_MOUSEBUTTONUP
:
565 if (_rightclick_emulate
) {
566 _right_button_down
= false;
567 _left_button_down
= false;
568 _left_button_clicked
= false;
569 } else if (ev
.button
.button
== SDL_BUTTON_LEFT
) {
570 _left_button_down
= false;
571 _left_button_clicked
= false;
572 } else if (ev
.button
.button
== SDL_BUTTON_RIGHT
) {
573 _right_button_down
= false;
578 case SDL_ACTIVEEVENT
:
579 if (!(ev
.active
.state
& SDL_APPMOUSEFOCUS
)) break;
581 if (ev
.active
.gain
) { // mouse entered the window, enable cursor
582 _cursor
.in_window
= true;
584 UndrawMouseCursor(); // mouse left the window, undraw cursor
585 _cursor
.in_window
= false;
590 HandleExitGameRequest();
593 case SDL_KEYDOWN
: // Toggle full-screen on ALT + ENTER/F
594 if ((ev
.key
.keysym
.mod
& (KMOD_ALT
| KMOD_META
)) &&
595 (ev
.key
.keysym
.sym
== SDLK_RETURN
|| ev
.key
.keysym
.sym
== SDLK_f
)) {
596 ToggleFullScreen(!_fullscreen
);
599 uint keycode
= ConvertSdlKeyIntoMy(&ev
.key
.keysym
, &character
);
600 HandleKeypress(keycode
, character
);
604 case SDL_VIDEORESIZE
: {
605 int w
= max(ev
.resize
.w
, 64);
606 int h
= max(ev
.resize
.h
, 64);
607 CreateMainSurface(w
, h
);
610 case SDL_VIDEOEXPOSE
: {
611 /* Force a redraw of the entire screen. Note
612 * that SDL 1.2 seems to do this automatically
613 * in most cases, but 1.3 / 2.0 does not. */
614 _num_dirty_rects
= MAX_DIRTY_RECTS
+ 1;
621 const char *VideoDriver_SDL::Start(const char * const *parm
)
624 _use_hwpalette
= GetDriverParamInt(parm
, "hw_palette", 2);
626 const char *s
= SdlOpen(SDL_INIT_VIDEO
);
627 if (s
!= NULL
) return s
;
630 if (!CreateMainSurface(_cur_resolution
.width
, _cur_resolution
.height
)) {
631 return SDL_CALL
SDL_GetError();
634 SDL_CALL
SDL_VideoDriverName(buf
, sizeof buf
);
635 DEBUG(driver
, 1, "SDL: using driver '%s'", buf
);
637 MarkWholeScreenDirty();
640 _draw_threaded
= GetDriverParam(parm
, "no_threads") == NULL
&& GetDriverParam(parm
, "no_thread") == NULL
;
645 void VideoDriver_SDL::SetupKeyboard()
647 SDL_CALL
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY
, SDL_DEFAULT_REPEAT_INTERVAL
);
648 SDL_CALL
SDL_EnableUNICODE(1);
651 void VideoDriver_SDL::Stop()
653 SdlClose(SDL_INIT_VIDEO
);
656 void VideoDriver_SDL::MainLoop()
658 uint32 cur_ticks
= SDL_CALL
SDL_GetTicks();
659 uint32 last_cur_ticks
= cur_ticks
;
660 uint32 next_tick
= cur_ticks
+ MILLISECONDS_PER_TICK
;
667 if (_draw_threaded
) {
668 /* Initialise the mutex first, because that's the thing we *need*
669 * directly in the newly created thread. */
670 _draw_mutex
= ThreadMutex::New();
671 if (_draw_mutex
== NULL
) {
672 _draw_threaded
= false;
674 _draw_mutex
->BeginCritical();
675 _draw_continue
= true;
677 _draw_threaded
= ThreadObject::New(&DrawSurfaceToScreenThread
, NULL
, &_draw_thread
, "ottd:draw-sdl");
679 /* Free the mutex if we won't be able to use it. */
680 if (!_draw_threaded
) {
681 _draw_mutex
->EndCritical();
685 /* Wait till the draw mutex has started itself. */
686 _draw_mutex
->WaitForSignal();
691 DEBUG(driver
, 1, "SDL: using %sthreads", _draw_threaded
? "" : "no ");
694 uint32 prev_cur_ticks
= cur_ticks
; // to check for wrapping
695 InteractiveRandom(); // randomness
697 while (PollEvent() == -1) {}
698 if (_exit_game
) break;
700 mod
= SDL_CALL
SDL_GetModState();
701 #if SDL_VERSION_ATLEAST(1, 3, 0)
702 keys
= SDL_CALL
SDL_GetKeyboardState(&numkeys
);
704 keys
= SDL_CALL
SDL_GetKeyState(&numkeys
);
709 /* Speedup when pressing tab, except when using ALT+TAB
710 * to switch to another application */
711 #if SDL_VERSION_ATLEAST(1, 3, 0)
712 if (keys
[SDL_SCANCODE_TAB
] && (mod
& KMOD_ALT
) == 0)
714 if (keys
[SDLK_TAB
] && (mod
& KMOD_ALT
) == 0)
715 #endif /* SDL_VERSION_ATLEAST(1, 3, 0) */
716 #endif /* defined(_DEBUG) */
718 if (!_networking
&& _game_mode
!= GM_MENU
) _fast_forward
|= 2;
719 } else if (_fast_forward
& 2) {
723 cur_ticks
= SDL_CALL
SDL_GetTicks();
724 if (cur_ticks
>= next_tick
|| (_fast_forward
&& !_pause_mode
) || cur_ticks
< prev_cur_ticks
) {
725 _realtime_tick
+= cur_ticks
- last_cur_ticks
;
726 last_cur_ticks
= cur_ticks
;
727 next_tick
= cur_ticks
+ MILLISECONDS_PER_TICK
;
729 bool old_ctrl_pressed
= _ctrl_pressed
;
731 _ctrl_pressed
= !!(mod
& KMOD_CTRL
);
732 _shift_pressed
= !!(mod
& KMOD_SHIFT
);
734 /* determine which directional keys are down */
736 #if SDL_VERSION_ATLEAST(1, 3, 0)
737 (keys
[SDL_SCANCODE_LEFT
] ? 1 : 0) |
738 (keys
[SDL_SCANCODE_UP
] ? 2 : 0) |
739 (keys
[SDL_SCANCODE_RIGHT
] ? 4 : 0) |
740 (keys
[SDL_SCANCODE_DOWN
] ? 8 : 0);
742 (keys
[SDLK_LEFT
] ? 1 : 0) |
743 (keys
[SDLK_UP
] ? 2 : 0) |
744 (keys
[SDLK_RIGHT
] ? 4 : 0) |
745 (keys
[SDLK_DOWN
] ? 8 : 0);
747 if (old_ctrl_pressed
!= _ctrl_pressed
) HandleCtrlChanged();
749 /* The gameloop is the part that can run asynchronously. The rest
750 * except sleeping can't. */
751 if (_draw_mutex
!= NULL
) _draw_mutex
->EndCritical();
755 if (_draw_mutex
!= NULL
) _draw_mutex
->BeginCritical();
758 _local_palette
= _cur_palette
;
760 /* Release the thread while sleeping */
761 if (_draw_mutex
!= NULL
) _draw_mutex
->EndCritical();
763 if (_draw_mutex
!= NULL
) _draw_mutex
->BeginCritical();
765 NetworkDrawChatMessage();
769 /* End of the critical part. */
770 if (_draw_mutex
!= NULL
&& !HasModalProgress()) {
771 _draw_mutex
->SendSignal();
773 /* Oh, we didn't have threads, then just draw unthreaded */
775 DrawSurfaceToScreen();
779 if (_draw_mutex
!= NULL
) {
780 _draw_continue
= false;
781 /* Sending signal if there is no thread blocked
782 * is very valid and results in noop */
783 _draw_mutex
->SendSignal();
784 _draw_mutex
->EndCritical();
785 _draw_thread
->Join();
795 bool VideoDriver_SDL::ChangeResolution(int w
, int h
)
797 if (_draw_mutex
!= NULL
) _draw_mutex
->BeginCritical(true);
798 bool ret
= CreateMainSurface(w
, h
);
799 if (_draw_mutex
!= NULL
) _draw_mutex
->EndCritical(true);
803 bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen
)
805 if (_draw_mutex
!= NULL
) _draw_mutex
->BeginCritical(true);
806 _fullscreen
= fullscreen
;
807 GetVideoModes(); // get the list of available video modes
808 bool ret
= _num_resolutions
!= 0 && CreateMainSurface(_cur_resolution
.width
, _cur_resolution
.height
);
811 /* switching resolution failed, put back full_screen to original status */
815 if (_draw_mutex
!= NULL
) _draw_mutex
->EndCritical(true);
819 bool VideoDriver_SDL::AfterBlitterChange()
821 return CreateMainSurface(_screen
.width
, _screen
.height
);
824 void VideoDriver_SDL::AcquireBlitterLock()
826 if (_draw_mutex
!= NULL
) _draw_mutex
->BeginCritical(true);
829 void VideoDriver_SDL::ReleaseBlitterLock()
831 if (_draw_mutex
!= NULL
) _draw_mutex
->EndCritical(true);
834 #endif /* WITH_SDL */