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" 27 #include <condition_variable> 30 #include "../safeguards.h" 34 static SDL_Surface *_sdl_screen;
35 static SDL_Surface *_sdl_realscreen;
36 static bool _all_modes;
48 #define MAX_DIRTY_RECTS 100 49 static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
50 static int _num_dirty_rects;
51 static int _use_hwpalette;
52 static int _requested_hwpalette;
56 if (_num_dirty_rects < MAX_DIRTY_RECTS) {
57 _dirty_rects[_num_dirty_rects].x = left;
58 _dirty_rects[_num_dirty_rects].y = top;
59 _dirty_rects[_num_dirty_rects].w = width;
60 _dirty_rects[_num_dirty_rects].h = height;
65 static void UpdatePalette(
bool init =
false)
69 for (
int i = 0; i != _local_palette.
count_dirty; i++) {
78 if (_sdl_screen != _sdl_realscreen && init) {
102 if (_sdl_screen != _sdl_realscreen && !init) {
113 SDL_BlitSurface(_sdl_screen,
nullptr, _sdl_realscreen,
nullptr);
114 SDL_UpdateRect(_sdl_realscreen, 0, 0, 0, 0);
118 static void InitPalette()
126 static void CheckPaletteAnim()
150 static void DrawSurfaceToScreen()
154 int n = _num_dirty_rects;
157 _num_dirty_rects = 0;
158 if (n > MAX_DIRTY_RECTS) {
159 if (_sdl_screen != _sdl_realscreen) {
160 SDL_BlitSurface(_sdl_screen,
nullptr, _sdl_realscreen,
nullptr);
162 SDL_UpdateRect(_sdl_realscreen, 0, 0, 0, 0);
164 if (_sdl_screen != _sdl_realscreen) {
165 for (
int i = 0; i < n; i++) {
166 SDL_BlitSurface(_sdl_screen, &_dirty_rects[i], _sdl_realscreen, &_dirty_rects[i]);
169 SDL_UpdateRects(_sdl_realscreen, n, _dirty_rects);
173 static void DrawSurfaceToScreenThread()
185 DrawSurfaceToScreen();
190 static const Dimension _default_resolutions[] = {
204 static void GetVideoModes()
206 SDL_Rect **modes = SDL_ListModes(
nullptr, SDL_SWSURFACE | SDL_FULLSCREEN);
207 if (modes ==
nullptr)
usererror(
"sdl: no modes available");
211 _all_modes = (SDL_ListModes(
nullptr, SDL_SWSURFACE | (_fullscreen ? SDL_FULLSCREEN : 0)) == (
void*)-1);
212 if (modes == (
void*)-1) {
213 for (uint i = 0; i <
lengthof(_default_resolutions); i++) {
214 if (SDL_VideoModeOK(_default_resolutions[i].width, _default_resolutions[i].height, 8, SDL_FULLSCREEN) != 0) {
219 for (
int i = 0; modes[i]; i++) {
220 uint w = modes[i]->w;
221 uint h = modes[i]->h;
222 if (w < 640 || h < 480)
continue;
231 static void GetAvailableVideoMode(uint *w, uint *h)
244 if (newdelta < delta) {
253 bool VideoDriver_SDL::CreateMainSurface(uint w, uint h)
255 SDL_Surface *newscreen, *icon;
260 GetAvailableVideoMode(&w, &h);
262 DEBUG(driver, 1,
"SDL: using mode %ux%ux%d", w, h, bpp);
264 if (bpp == 0)
usererror(
"Can't use a blitter that blits 0 bpp for normal visuals");
266 char icon_path[MAX_PATH];
269 icon = SDL_LoadBMP(icon_path);
270 if (icon !=
nullptr) {
272 uint32 rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
274 SDL_SetColorKey(icon, SDL_SRCCOLORKEY, rgbmap);
275 SDL_WM_SetIcon(icon,
nullptr);
276 SDL_FreeSurface(icon);
280 if (_use_hwpalette == 2) {
302 want_hwpalette = bpp == 8 && _fullscreen && _support8bpp ==
S8BPP_HARDWARE;
305 want_hwpalette = _use_hwpalette;
308 if (want_hwpalette)
DEBUG(driver, 1,
"SDL: requesting hardware palette");
311 if (_sdl_screen !=
nullptr && _sdl_screen != _sdl_realscreen) SDL_FreeSurface(_sdl_screen);
313 if (_sdl_realscreen !=
nullptr) {
314 if (_requested_hwpalette != want_hwpalette) {
323 DEBUG(driver, 0,
"SDL: Restarting SDL video subsystem, to force hwpalette change");
324 SDL_QuitSubSystem(SDL_INIT_VIDEO);
325 SDL_InitSubSystem(SDL_INIT_VIDEO);
334 _requested_hwpalette = want_hwpalette;
337 newscreen = SDL_SetVideoMode(w, h, bpp, SDL_SWSURFACE | (want_hwpalette ? SDL_HWPALETTE : 0) | (_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE));
338 if (newscreen ==
nullptr) {
339 DEBUG(driver, 0,
"SDL: Couldn't allocate a window to draw on");
342 _sdl_realscreen = newscreen;
344 if (bpp == 8 && (_sdl_realscreen->flags & SDL_HWPALETTE) != SDL_HWPALETTE) {
363 DEBUG(driver, 1,
"SDL: using shadow surface");
364 newscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp, 0, 0, 0, 0);
365 if (newscreen ==
nullptr) {
366 DEBUG(driver, 0,
"SDL: Couldn't allocate a shadow surface to draw on");
372 _num_dirty_rects = 0;
374 _screen.width = newscreen->w;
375 _screen.height = newscreen->h;
376 _screen.pitch = newscreen->pitch / (bpp / 8);
377 _screen.dst_ptr = newscreen->pixels;
378 _sdl_screen = newscreen;
383 if (_fullscreen) _cursor.
in_window =
true;
390 seprintf(caption,
lastof(caption),
"OpenTTD %s", _openttd_revision);
391 SDL_WM_SetCaption(caption, caption);
398 bool VideoDriver_SDL::ClaimMousePointer()
405 #if SDL_VERSION_ATLEAST(1, 3, 0) 414 #define AS(x, z) {x, 0, z} 415 #define AM(x, y, z, w) {x, (byte)(y - x), z} 419 AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),
421 AS(SDLK_DOWN, WKC_DOWN),
422 AS(SDLK_LEFT, WKC_LEFT),
423 AS(SDLK_RIGHT, WKC_RIGHT),
425 AS(SDLK_HOME, WKC_HOME),
426 AS(SDLK_END, WKC_END),
428 AS(SDLK_INSERT, WKC_INSERT),
429 AS(SDLK_DELETE, WKC_DELETE),
432 AM(SDLK_a, SDLK_z,
'A',
'Z'),
433 AM(SDLK_0, SDLK_9,
'0',
'9'),
435 AS(SDLK_ESCAPE, WKC_ESC),
436 AS(SDLK_PAUSE, WKC_PAUSE),
437 AS(SDLK_BACKSPACE, WKC_BACKSPACE),
439 AS(SDLK_SPACE, WKC_SPACE),
440 AS(SDLK_RETURN, WKC_RETURN),
441 AS(SDLK_TAB, WKC_TAB),
444 AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
447 AM(SDLK_KP0, SDLK_KP9,
'0',
'9'),
448 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
449 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
450 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
451 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
452 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
453 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
469 static uint ConvertSdlKeyIntoMy(SDL_keysym *sym,
WChar *character)
474 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
475 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
476 key = sym->sym - map->vk_from + map->map_to;
482 #if defined(_WIN32) || defined(__OS2__) 483 if (sym->scancode == 41) key = WKC_BACKQUOTE;
484 #elif defined(__APPLE__) 485 if (sym->scancode == 10) key = WKC_BACKQUOTE;
486 #elif defined(__SVR4) && defined(__sun) 487 if (sym->scancode == 60) key = WKC_BACKQUOTE;
488 if (sym->scancode == 49) key = WKC_BACKSPACE;
489 #elif defined(__sgi__) 490 if (sym->scancode == 22) key = WKC_BACKQUOTE;
492 if (sym->scancode == 49) key = WKC_BACKQUOTE;
496 if (sym->mod & KMOD_META) key |= WKC_META;
497 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
498 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
499 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
501 *character = sym->unicode;
505 int VideoDriver_SDL::PollEvent()
509 if (!SDL_PollEvent(&ev))
return -2;
512 case SDL_MOUSEMOTION:
514 SDL_WarpMouse(_cursor.
pos.x, _cursor.
pos.y);
519 case SDL_MOUSEBUTTONDOWN:
521 ev.button.button = SDL_BUTTON_RIGHT;
524 switch (ev.button.button) {
525 case SDL_BUTTON_LEFT:
529 case SDL_BUTTON_RIGHT:
534 case SDL_BUTTON_WHEELUP: _cursor.
wheel--;
break;
535 case SDL_BUTTON_WHEELDOWN: _cursor.
wheel++;
break;
542 case SDL_MOUSEBUTTONUP:
547 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
550 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
556 case SDL_ACTIVEEVENT:
557 if (!(ev.active.state & SDL_APPMOUSEFOCUS))
break;
559 if (ev.active.gain) {
568 HandleExitGameRequest();
572 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_META)) &&
573 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
574 ToggleFullScreen(!_fullscreen);
577 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
582 case SDL_VIDEORESIZE: {
583 int w =
max(ev.resize.w, 64);
584 int h =
max(ev.resize.h, 64);
585 CreateMainSurface(w, h);
588 case SDL_VIDEOEXPOSE: {
592 _num_dirty_rects = MAX_DIRTY_RECTS + 1;
608 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
609 ret_code = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
610 }
else if (SDL_WasInit(SDL_INIT_VIDEO) == 0) {
611 ret_code = SDL_InitSubSystem(SDL_INIT_VIDEO);
613 if (ret_code == -1)
return SDL_GetError();
617 return SDL_GetError();
620 SDL_VideoDriverName(buf,
sizeof buf);
621 DEBUG(driver, 1,
"SDL: using driver '%s'", buf);
631 void VideoDriver_SDL::SetupKeyboard()
633 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
634 SDL_EnableUNICODE(1);
639 SDL_QuitSubSystem(SDL_INIT_VIDEO);
640 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
647 uint32 cur_ticks = SDL_GetTicks();
648 uint32 last_cur_ticks = cur_ticks;
656 std::thread draw_thread;
657 std::unique_lock<std::recursive_mutex> draw_lock;
665 draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
677 _draw_mutex =
nullptr;
689 uint32 prev_cur_ticks = cur_ticks;
692 while (PollEvent() == -1) {}
693 if (_exit_game)
break;
695 mod = SDL_GetModState();
696 #if SDL_VERSION_ATLEAST(1, 3, 0) 697 keys = SDL_GetKeyboardState(&numkeys);
699 keys = SDL_GetKeyState(&numkeys);
706 #if SDL_VERSION_ATLEAST(1, 3, 0) 707 if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
709 if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
713 if (!
_networking && _game_mode != GM_MENU) _fast_forward |= 2;
714 }
else if (_fast_forward & 2) {
718 cur_ticks = SDL_GetTicks();
719 if (cur_ticks >= next_tick || (_fast_forward && !
_pause_mode) || cur_ticks < prev_cur_ticks) {
721 last_cur_ticks = cur_ticks;
731 #if SDL_VERSION_ATLEAST(1, 3, 0) 732 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
733 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
734 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
735 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
737 (keys[SDLK_LEFT] ? 1 : 0) |
738 (keys[SDLK_UP] ? 2 : 0) |
739 (keys[SDLK_RIGHT] ? 4 : 0) |
740 (keys[SDLK_DOWN] ? 8 : 0);
770 DrawSurfaceToScreen();
779 if (draw_lock.owns_lock()) draw_lock.unlock();
793 std::unique_lock<std::recursive_mutex>
lock;
794 if (
_draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
796 return CreateMainSurface(w, h);
801 std::unique_lock<std::recursive_mutex>
lock;
802 if (
_draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
804 _fullscreen = fullscreen;
818 return CreateMainSurface(_screen.width, _screen.height);
const char * GetDriverParam(const char *const *parm, const char *name)
Get a string parameter the list of parameters.
bool _networking
are we in networking mode?
uint32 _realtime_tick
The real time in the game.
Point pos
logical mouse position
Information about the currently used palette.
void AcquireBlitterLock() override
Acquire any lock(s) required to be held when changing blitters.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
bool _right_button_down
Is right mouse button pressed?
void MakeDirty(int left, int top, int width, int height) override
Mark a particular area dirty.
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Base of the SDL video driver.
void Stop() override
Stop this driver.
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
Dimension _cur_resolution
The current resolution.
static volatile bool _draw_continue
Should we keep continue drawing?
#define lastof(x)
Get the last element of an fixed size array.
How all blitters should look like.
Subdirectory for all base data (base sets, intro game)
static T max(const T a, const T b)
Returns the maximum of two values.
virtual void PostResize()
Post resize event.
Palette animation should be done by video backend (8bpp only!)
bool _left_button_clicked
Is left mouse button clicked?
std::vector< Dimension > _resolutions
List of resolutions.
bool AfterBlitterChange() override
Callback invoked after the blitter was changed.
static std::condition_variable_any * _draw_signal
Signal to draw the next frame.
bool _ctrl_pressed
Is Ctrl pressed?
bool StartNewThread(std::thread *thr, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
bool _right_button_clicked
Is right mouse button clicked?
The blitter takes care of the palette animation.
char * FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename)
Find a path to the filename in one of the search directories.
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
bool _left_button_down
Is left mouse button pressed?
std::mutex lock
synchronization for playback status fields
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
int wheel
mouse wheel movement
bool UpdateCursorPosition(int x, int y, bool queued_warp)
Update cursor position on mouse movement.
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
void HandleKeypress(uint keycode, WChar key)
Handle keyboard input.
byte _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
void HandleMouseEvents()
Handle a mouse event from the video driver.
#define lengthof(x)
Return the length of an fixed size array.
bool ToggleFullscreen(bool fullscreen) override
Change the full screen setting.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
int first_dirty
The first dirty element.
PauseMode _pause_mode
The current pause mode.
Palette _cur_palette
Current palette.
bool _shift_pressed
Is Shift pressed?
#define DEBUG(name, level,...)
Output a line of debugging information.
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
static bool _draw_threaded
Whether the drawing is/may be done in a separate thread.
void HandleCtrlChanged()
State of CONTROL key has changed.
void MainLoop() override
Perform the actual drawing.
Speed of painting drawn video buffer.
void NetworkDrawChatMessage()
Draw the chat message-box.
static Palette _local_palette
Local copy of the palette for use in the drawing thread.
#define endof(x)
Get the end element of an fixed size array.
bool in_window
mouse inside this window, determines drawing logic
bool ChangeResolution(int w, int h) override
Change the resolution of the window.
virtual uint8 GetScreenDepth()=0
Get the screen depth this blitter works for.
#define AS(ap_name, size_x, size_y, min_year, max_year, catchment, noise, maint_cost, ttdpatch_type, class_id, name, preview)
AirportSpec definition for airports with at least one depot.
void ReleaseBlitterLock() override
Release any lock(s) required to be held when changing blitters.
int GetDriverParamInt(const char *const *parm, const char *name, int def)
Get an integer parameter the list of parameters.
static std::recursive_mutex * _draw_mutex
Mutex to keep the access to the shared memory controlled.
bool _rightclick_emulate
Whether right clicking is emulated.
void GameSizeChanged()
Size of the application screen changed.
Factory for the SDL video driver.
int count_dirty
The number of dirty elements.
static T Delta(const T a, const T b)
Returns the (absolute) difference between two (scalar) variables.
uint32 WChar
Type for wide characters, i.e.
Dimensions (a width and height) of a rectangle in 2D.
Full 8bpp support by OS and hardware.
static bool HasModalProgress()
Check if we are currently in a modal progress state.
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
const char * Start(const char *const *param) override
Start this driver.
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.