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;
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];
286 SDL_Surface *icon = SDL_LoadBMP(icon_path);
287 if (icon !=
nullptr) {
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());
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;
338 if (_fullscreen) _cursor.
in_window =
true;
350 bool VideoDriver_SDL::ClaimMousePointer()
362 SDL_StartTextInput();
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} 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),
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),
444 static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym,
WChar *character)
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;
459 if (sym->scancode == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
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;
468 if (sym->mod & KMOD_GUI ||
469 sym->mod & KMOD_CTRL ||
470 sym->mod & KMOD_ALT ||
472 *character = WKC_NONE;
474 *character = sym->sym;
484 static uint ConvertSdlKeycodeIntoMy(SDL_Keycode kc)
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;
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:
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:
528 ev.button.button = SDL_BUTTON_RIGHT;
531 switch (ev.button.button) {
532 case SDL_BUTTON_LEFT:
536 case SDL_BUTTON_RIGHT:
546 case SDL_MOUSEBUTTONUP:
551 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
554 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
561 HandleExitGameRequest();
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);
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) ||
593 case SDL_TEXTINPUT: {
595 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
596 uint keycode = ConvertSdlKeycodeIntoMy(kc);
607 case SDL_WINDOWEVENT: {
608 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
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) {
618 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
634 SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION ,
"0");
640 if (SDL_WasInit(SDL_INIT_VIDEO) == 0) {
641 ret_code = SDL_InitSubSystem(SDL_INIT_VIDEO);
643 if (ret_code < 0)
return SDL_GetError();
647 return SDL_GetError();
650 const char *dname = SDL_GetVideoDriver(0);
651 DEBUG(driver, 1,
"SDL2: using driver '%s'", dname);
665 SDL_QuitSubSystem(SDL_INIT_VIDEO);
666 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
673 uint32 cur_ticks = SDL_GetTicks();
674 uint32 last_cur_ticks = cur_ticks;
682 std::thread draw_thread;
683 std::unique_lock<std::recursive_mutex> draw_lock;
691 draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
703 _draw_mutex =
nullptr;
715 uint32 prev_cur_ticks = cur_ticks;
718 while (PollEvent() == -1) {}
719 if (_exit_game)
break;
721 mod = SDL_GetModState();
722 keys = SDL_GetKeyboardState(&numkeys);
729 if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
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) {
740 last_cur_ticks = cur_ticks;
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);
758 if (_draw_mutex !=
nullptr) draw_lock.unlock();
762 if (_draw_mutex !=
nullptr) draw_lock.lock();
768 if (_draw_mutex !=
nullptr) draw_lock.unlock();
770 if (_draw_mutex !=
nullptr) draw_lock.lock();
782 DrawSurfaceToScreen();
786 if (_draw_mutex !=
nullptr) {
791 if (draw_lock.owns_lock()) draw_lock.unlock();
798 _draw_mutex =
nullptr;
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);
813 std::unique_lock<std::recursive_mutex>
lock;
814 if (_draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
818 SDL_GetWindowSize(_sdl_window, &_window_size_w, &_window_size_h);
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);
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());
845 SDL_GetWindowSize(_sdl_window, &w, &h);
846 return CreateMainSurface(w, h,
false);
851 if (_draw_mutex !=
nullptr) _draw_mutex->lock();
856 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.