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" 35 static SDL_Window *_sdl_window;
36 static SDL_Surface *_sdl_surface;
37 static SDL_Surface *_sdl_realscreen;
44 static std::condition_variable_any *
_draw_signal =
nullptr;
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;
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++) {
81 SDL_SetSurfacePalette(_sdl_surface, _sdl_palette);
83 if (_sdl_surface != _sdl_realscreen && init) {
104 SDL_SetSurfacePalette(_sdl_realscreen, _sdl_palette);
107 if (_sdl_surface != _sdl_realscreen && !init) {
118 SDL_BlitSurface(_sdl_surface,
nullptr, _sdl_realscreen,
nullptr);
119 SDL_UpdateWindowSurface(_sdl_window);
123 static void InitPalette()
131 static void CheckPaletteAnim()
155 static void DrawSurfaceToScreen()
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()
195 DrawSurfaceToScreen();
200 static void GetVideoModes()
202 int modes = SDL_GetNumDisplayModes(0);
203 if (modes == 0)
usererror(
"sdl: no modes available");
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;
223 static void GetAvailableVideoMode(uint *w, uint *h)
236 if (newdelta < delta) {
245 bool VideoDriver_SDL::CreateMainSurface(uint w, uint h,
bool resize)
247 SDL_Surface *newscreen;
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");
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 | SDL_WINDOW_RESIZABLE;
266 flags |= SDL_WINDOW_FULLSCREEN;
269 _sdl_window = SDL_CreateWindow(
271 SDL_WINDOWPOS_UNDEFINED,
272 SDL_WINDOWPOS_UNDEFINED,
276 if (_sdl_window ==
nullptr) {
277 DEBUG(driver, 0,
"SDL2: Couldn't allocate a window to draw on");
281 char icon_path[MAX_PATH];
284 SDL_Surface *icon = SDL_LoadBMP(icon_path);
285 if (icon !=
nullptr) {
287 uint32 rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
289 SDL_SetColorKey(icon, SDL_TRUE, rgbmap);
290 SDL_SetWindowIcon(_sdl_window, icon);
291 SDL_FreeSurface(icon);
296 if (resize) SDL_SetWindowSize(_sdl_window, w, h);
298 newscreen = SDL_GetWindowSurface(_sdl_window);
299 if (newscreen == NULL) {
300 DEBUG(driver, 0,
"SDL2: Couldn't get window surface: %s", SDL_GetError());
304 _sdl_realscreen = newscreen;
307 newscreen = SDL_CreateRGBSurface(0, w, h, 8, 0, 0, 0, 0);
309 if (newscreen ==
nullptr) {
310 DEBUG(driver, 0,
"SDL2: Couldn't allocate shadow surface: %s", SDL_GetError());
315 if (_sdl_palette ==
nullptr) {
316 _sdl_palette = SDL_AllocPalette(256);
319 if (_sdl_palette ==
nullptr) {
320 DEBUG(driver, 0,
"SDL_AllocPalette() failed: %s", SDL_GetError());
325 _num_dirty_rects = 0;
327 _screen.width = newscreen->w;
328 _screen.height = newscreen->h;
329 _screen.pitch = newscreen->pitch / (bpp / 8);
330 _screen.dst_ptr = newscreen->pixels;
331 _sdl_surface = newscreen;
336 if (_fullscreen) _cursor.
in_window =
true;
348 bool VideoDriver_SDL::ClaimMousePointer()
360 SDL_StartTextInput();
384 #define AS(x, z) {x, 0, z, false} 385 #define AM(x, y, z, w) {x, (byte)(y - x), z, false} 386 #define AS_UP(x, z) {x, 0, z, true} 387 #define AM_UP(x, y, z, w) {x, (byte)(y - x), z, true} 391 AS_UP(SDLK_PAGEUP, WKC_PAGEUP),
392 AS_UP(SDLK_PAGEDOWN, WKC_PAGEDOWN),
393 AS_UP(SDLK_UP, WKC_UP),
394 AS_UP(SDLK_DOWN, WKC_DOWN),
395 AS_UP(SDLK_LEFT, WKC_LEFT),
396 AS_UP(SDLK_RIGHT, WKC_RIGHT),
398 AS_UP(SDLK_HOME, WKC_HOME),
399 AS_UP(SDLK_END, WKC_END),
401 AS_UP(SDLK_INSERT, WKC_INSERT),
402 AS_UP(SDLK_DELETE, WKC_DELETE),
405 AM(SDLK_a, SDLK_z,
'A',
'Z'),
406 AM(SDLK_0, SDLK_9,
'0',
'9'),
408 AS_UP(SDLK_ESCAPE, WKC_ESC),
409 AS_UP(SDLK_PAUSE, WKC_PAUSE),
410 AS_UP(SDLK_BACKSPACE, WKC_BACKSPACE),
412 AS(SDLK_SPACE, WKC_SPACE),
413 AS(SDLK_RETURN, WKC_RETURN),
414 AS(SDLK_TAB, WKC_TAB),
417 AM_UP(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
420 AM(SDLK_KP_0, SDLK_KP_9,
'0',
'9'),
421 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
422 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
423 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
424 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
425 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
426 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
442 static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym,
WChar *character)
446 bool unprintable =
false;
448 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
449 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
450 key = sym->sym - map->vk_from + map->map_to;
451 unprintable = map->unprintable;
457 if (sym->scancode == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
460 if (sym->mod & KMOD_GUI) key |= WKC_META;
461 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
462 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
463 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
466 if (sym->mod & KMOD_GUI ||
467 sym->mod & KMOD_CTRL ||
468 sym->mod & KMOD_ALT ||
470 *character = WKC_NONE;
472 *character = sym->sym;
482 static uint ConvertSdlKeycodeIntoMy(SDL_Keycode kc)
487 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
488 if ((uint)(kc - map->vk_from) <= map->vk_count) {
489 key = kc - map->vk_from + map->map_to;
496 SDL_Scancode sc = SDL_GetScancodeFromKey(kc);
497 if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
502 int VideoDriver_SDL::PollEvent()
506 if (!SDL_PollEvent(&ev))
return -2;
509 case SDL_MOUSEMOTION:
511 SDL_WarpMouseInWindow(_sdl_window, _cursor.
pos.x, _cursor.
pos.y);
517 if (ev.wheel.y > 0) {
519 }
else if (ev.wheel.y < 0) {
524 case SDL_MOUSEBUTTONDOWN:
526 ev.button.button = SDL_BUTTON_RIGHT;
529 switch (ev.button.button) {
530 case SDL_BUTTON_LEFT:
534 case SDL_BUTTON_RIGHT:
544 case SDL_MOUSEBUTTONUP:
549 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
552 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
559 HandleExitGameRequest();
563 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_GUI)) &&
564 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
565 if (ev.key.repeat == 0) ToggleFullScreen(!_fullscreen);
569 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
573 keycode == WKC_DELETE ||
574 keycode == WKC_NUM_ENTER ||
575 keycode == WKC_LEFT ||
576 keycode == WKC_RIGHT ||
578 keycode == WKC_DOWN ||
579 keycode == WKC_HOME ||
580 keycode == WKC_END ||
581 keycode & WKC_META ||
582 keycode & WKC_CTRL ||
584 (keycode >= WKC_F1 && keycode <= WKC_F12) ||
591 case SDL_TEXTINPUT: {
593 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
594 uint keycode = ConvertSdlKeycodeIntoMy(kc);
605 case SDL_WINDOWEVENT: {
606 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
608 _num_dirty_rects = MAX_DIRTY_RECTS + 1;
609 }
else if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
610 int w =
max(ev.window.data1, 64);
611 int h =
max(ev.window.data2, 64);
612 CreateMainSurface(w, h, w != ev.window.data1 || h != ev.window.data2);
613 }
else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
616 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
632 SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION ,
"0");
638 if (SDL_WasInit(SDL_INIT_VIDEO) == 0) {
639 ret_code = SDL_InitSubSystem(SDL_INIT_VIDEO);
641 if (ret_code < 0)
return SDL_GetError();
645 return SDL_GetError();
648 const char *dname = SDL_GetCurrentVideoDriver();
649 DEBUG(driver, 1,
"SDL2: using driver '%s'", dname);
663 SDL_QuitSubSystem(SDL_INIT_VIDEO);
664 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
671 uint32 cur_ticks = SDL_GetTicks();
672 uint32 last_cur_ticks = cur_ticks;
680 std::thread draw_thread;
681 std::unique_lock<std::recursive_mutex> draw_lock;
689 draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
701 _draw_mutex =
nullptr;
713 uint32 prev_cur_ticks = cur_ticks;
716 while (PollEvent() == -1) {}
717 if (_exit_game)
break;
719 mod = SDL_GetModState();
720 keys = SDL_GetKeyboardState(&numkeys);
727 if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
730 if (!
_networking && _game_mode != GM_MENU) _fast_forward |= 2;
731 }
else if (_fast_forward & 2) {
735 cur_ticks = SDL_GetTicks();
736 if (SDL_TICKS_PASSED(cur_ticks, next_tick) || (_fast_forward && !
_pause_mode) || cur_ticks < prev_cur_ticks) {
738 last_cur_ticks = cur_ticks;
748 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
749 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
750 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
751 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
756 if (_draw_mutex !=
nullptr) draw_lock.unlock();
760 if (_draw_mutex !=
nullptr) draw_lock.lock();
766 if (_draw_mutex !=
nullptr) draw_lock.unlock();
768 if (_draw_mutex !=
nullptr) draw_lock.lock();
780 DrawSurfaceToScreen();
784 if (_draw_mutex !=
nullptr) {
789 if (draw_lock.owns_lock()) draw_lock.unlock();
796 _draw_mutex =
nullptr;
803 std::unique_lock<std::recursive_mutex>
lock;
804 if (_draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
806 return CreateMainSurface(w, h,
true);
811 std::unique_lock<std::recursive_mutex>
lock;
812 if (_draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
816 SDL_GetWindowSize(_sdl_window, &_window_size_w, &_window_size_h);
820 if (SDL_GetCurrentDisplayMode(0, &dm) < 0) {
821 DEBUG(driver, 0,
"SDL_GetCurrentDisplayMode() failed: %s", SDL_GetError());
823 SDL_SetWindowSize(_sdl_window, dm.w, dm.h);
827 DEBUG(driver, 1,
"SDL2: Setting %s", fullscreen ?
"fullscreen" :
"windowed");
828 int ret = SDL_SetWindowFullscreen(_sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
831 _fullscreen = fullscreen;
832 if (!fullscreen) SDL_SetWindowSize(_sdl_window, _window_size_w, _window_size_h);
834 DEBUG(driver, 0,
"SDL_SetWindowFullscreen() failed: %s", SDL_GetError());
843 SDL_GetWindowSize(_sdl_window, &w, &h);
844 return CreateMainSurface(w, h,
false);
849 if (_draw_mutex !=
nullptr) _draw_mutex->lock();
854 if (_draw_mutex !=
nullptr) _draw_mutex->unlock();
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!
void Stop() override
Stop this driver.
void CSleep(int milliseconds)
Sleep on the current thread for a defined time.
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
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.
Base of the SDL2 video driver.
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 IsValidChar(WChar key, CharSetFilter afilter)
Only allow certain keys.
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.
void EditBoxLostFocus() override
An edit box lost the input focus.
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.
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.
Both numeric and alphabetic and spaces and stuff.
void MainLoop() override
Perform the actual drawing.
Speed of painting drawn video buffer.
bool edit_box_focused
This is true to indicate that keyboard input is in text input mode, and SDL_TEXTINPUT events are enab...
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.
void EditBoxGainedFocus() override
An edit box gained the input focus.
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.
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.
void HandleTextInput(const char *str, bool marked=false, const char *caret=nullptr, const char *insert_location=nullptr, const char *replacement_end=nullptr)
Handle text input.
bool FocusedWindowIsConsole()
Check if a console is focused.
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.
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.