14 #include "../stdafx.h" 15 #include "../openttd.h" 16 #include "../gfx_func.h" 18 #include "../blitter/factory.hpp" 19 #include "../network/network.h" 20 #include "../thread.h" 21 #include "../progress.h" 22 #include "../core/random_func.hpp" 23 #include "../core/math_func.hpp" 24 #include "../fileio_func.h" 25 #include "../framerate_type.h" 29 #include <condition_variable> 32 #include "../safeguards.h" 36 static SDL_Window *_sdl_window;
37 static SDL_Surface *_sdl_surface;
38 static SDL_Surface *_sdl_realscreen;
45 static std::condition_variable_any *
_draw_signal =
nullptr;
49 static SDL_Palette *_sdl_palette;
51 #define MAX_DIRTY_RECTS 100 52 static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
53 static int _num_dirty_rects;
56 static int _window_size_w;
57 static int _window_size_h;
61 if (_num_dirty_rects < MAX_DIRTY_RECTS) {
62 _dirty_rects[_num_dirty_rects].x = left;
63 _dirty_rects[_num_dirty_rects].y = top;
64 _dirty_rects[_num_dirty_rects].w = width;
65 _dirty_rects[_num_dirty_rects].h = height;
70 static void UpdatePalette(
bool init =
false)
74 for (
int i = 0; i != _local_palette.
count_dirty; i++) {
82 SDL_SetSurfacePalette(_sdl_surface, _sdl_palette);
84 if (_sdl_surface != _sdl_realscreen && init) {
105 SDL_SetSurfacePalette(_sdl_realscreen, _sdl_palette);
108 if (_sdl_surface != _sdl_realscreen && !init) {
119 SDL_BlitSurface(_sdl_surface,
nullptr, _sdl_realscreen,
nullptr);
120 SDL_UpdateWindowSurface(_sdl_window);
124 static void InitPalette()
132 static void CheckPaletteAnim()
156 static void DrawSurfaceToScreen()
160 int n = _num_dirty_rects;
163 _num_dirty_rects = 0;
165 if (n > MAX_DIRTY_RECTS) {
166 if (_sdl_surface != _sdl_realscreen) {
167 SDL_BlitSurface(_sdl_surface,
nullptr, _sdl_realscreen,
nullptr);
170 SDL_UpdateWindowSurface(_sdl_window);
172 if (_sdl_surface != _sdl_realscreen) {
173 for (
int i = 0; i < n; i++) {
175 _sdl_surface, &_dirty_rects[i],
176 _sdl_realscreen, &_dirty_rects[i]);
180 SDL_UpdateWindowSurfaceRects(_sdl_window, _dirty_rects, n);
184 static void DrawSurfaceToScreenThread()
196 DrawSurfaceToScreen();
201 static void GetVideoModes()
203 int modes = SDL_GetNumDisplayModes(0);
204 if (modes == 0)
usererror(
"sdl: no modes available");
208 SDL_DisplayMode mode;
209 for (
int i = 0; i < modes; i++) {
210 SDL_GetDisplayMode(0, i, &mode);
215 if (w < 640 || h < 480)
continue;
224 static void GetAvailableVideoMode(uint *w, uint *h)
237 if (newdelta < delta) {
246 bool VideoDriver_SDL::CreateMainSurface(uint w, uint h,
bool resize)
248 SDL_Surface *newscreen;
252 GetAvailableVideoMode(&w, &h);
254 DEBUG(driver, 1,
"SDL2: using mode %ux%ux%d", w, h, bpp);
256 if (bpp == 0)
usererror(
"Can't use a blitter that blits 0 bpp for normal visuals");
259 if (_sdl_surface !=
nullptr && _sdl_surface != _sdl_realscreen) SDL_FreeSurface(_sdl_surface);
261 seprintf(caption,
lastof(caption),
"OpenTTD %s", _openttd_revision);
263 if (_sdl_window ==
nullptr) {
264 Uint32 flags = SDL_WINDOW_SHOWN;
267 flags |= SDL_WINDOW_FULLSCREEN;
269 flags |= SDL_WINDOW_RESIZABLE;
272 _sdl_window = SDL_CreateWindow(
274 SDL_WINDOWPOS_UNDEFINED,
275 SDL_WINDOWPOS_UNDEFINED,
279 if (_sdl_window ==
nullptr) {
280 DEBUG(driver, 0,
"SDL2: Couldn't allocate a window to draw on");
284 char icon_path[MAX_PATH];
287 SDL_Surface *icon = SDL_LoadBMP(icon_path);
288 if (icon !=
nullptr) {
290 uint32 rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
292 SDL_SetColorKey(icon, SDL_TRUE, rgbmap);
293 SDL_SetWindowIcon(_sdl_window, icon);
294 SDL_FreeSurface(icon);
299 if (resize) SDL_SetWindowSize(_sdl_window, w, h);
301 newscreen = SDL_GetWindowSurface(_sdl_window);
302 if (newscreen == NULL) {
303 DEBUG(driver, 0,
"SDL2: Couldn't get window surface: %s", SDL_GetError());
307 _sdl_realscreen = newscreen;
310 newscreen = SDL_CreateRGBSurface(0, w, h, 8, 0, 0, 0, 0);
312 if (newscreen ==
nullptr) {
313 DEBUG(driver, 0,
"SDL2: Couldn't allocate shadow surface: %s", SDL_GetError());
318 if (_sdl_palette ==
nullptr) {
319 _sdl_palette = SDL_AllocPalette(256);
322 if (_sdl_palette ==
nullptr) {
323 DEBUG(driver, 0,
"SDL_AllocPalette() failed: %s", SDL_GetError());
328 _num_dirty_rects = 0;
330 _screen.width = newscreen->w;
331 _screen.height = newscreen->h;
332 _screen.pitch = newscreen->pitch / (bpp / 8);
333 _screen.dst_ptr = newscreen->pixels;
334 _sdl_surface = newscreen;
339 if (_fullscreen) _cursor.
in_window =
true;
351 bool VideoDriver_SDL::ClaimMousePointer()
363 #define AS(x, z) {x, 0, z} 364 #define AM(x, y, z, w) {x, (byte)(y - x), z} 368 AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),
370 AS(SDLK_DOWN, WKC_DOWN),
371 AS(SDLK_LEFT, WKC_LEFT),
372 AS(SDLK_RIGHT, WKC_RIGHT),
374 AS(SDLK_HOME, WKC_HOME),
375 AS(SDLK_END, WKC_END),
377 AS(SDLK_INSERT, WKC_INSERT),
378 AS(SDLK_DELETE, WKC_DELETE),
381 AM(SDLK_a, SDLK_z,
'A',
'Z'),
382 AM(SDLK_0, SDLK_9,
'0',
'9'),
384 AS(SDLK_ESCAPE, WKC_ESC),
385 AS(SDLK_PAUSE, WKC_PAUSE),
386 AS(SDLK_BACKSPACE, WKC_BACKSPACE),
388 AS(SDLK_SPACE, WKC_SPACE),
389 AS(SDLK_RETURN, WKC_RETURN),
390 AS(SDLK_TAB, WKC_TAB),
393 AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
396 AM(SDLK_KP_0, SDLK_KP_9,
'0',
'9'),
397 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
398 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
399 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
400 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
401 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
402 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
418 static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym,
WChar *character)
423 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
424 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
425 key = sym->sym - map->vk_from + map->map_to;
431 #if defined(_WIN32) || defined(__OS2__) 432 if (sym->scancode == 41) key = WKC_BACKQUOTE;
433 #elif defined(__APPLE__) 434 if (sym->scancode == 10) key = WKC_BACKQUOTE;
435 #elif defined(__SVR4) && defined(__sun) 436 if (sym->scancode == 60) key = WKC_BACKQUOTE;
437 if (sym->scancode == 49) key = WKC_BACKSPACE;
438 #elif defined(__sgi__) 439 if (sym->scancode == 22) key = WKC_BACKQUOTE;
441 if (sym->scancode == 49) key = WKC_BACKQUOTE;
445 if (sym->mod & KMOD_GUI) key |= WKC_META;
446 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
447 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
448 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
451 if (sym->mod & KMOD_GUI ||
452 sym->mod & KMOD_SHIFT ||
453 sym->mod & KMOD_CTRL ||
454 sym->mod & KMOD_ALT) {
455 *character = WKC_NONE;
457 *character = sym->sym;
467 static uint ConvertSdlKeycodeIntoMy(SDL_Keycode kc)
472 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
473 if ((uint)(kc - map->vk_from) <= map->vk_count) {
474 key = kc - map->vk_from + map->map_to;
481 SDL_Scancode sc = SDL_GetScancodeFromKey(kc);
482 if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
487 int VideoDriver_SDL::PollEvent()
491 if (!SDL_PollEvent(&ev))
return -2;
494 case SDL_MOUSEMOTION:
496 SDL_WarpMouseInWindow(_sdl_window, _cursor.
pos.x, _cursor.
pos.y);
502 if (ev.wheel.y > 0) {
504 }
else if (ev.wheel.y < 0) {
509 case SDL_MOUSEBUTTONDOWN:
511 ev.button.button = SDL_BUTTON_RIGHT;
514 switch (ev.button.button) {
515 case SDL_BUTTON_LEFT:
519 case SDL_BUTTON_RIGHT:
529 case SDL_MOUSEBUTTONUP:
534 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
537 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
544 HandleExitGameRequest();
548 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_GUI)) &&
549 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
550 if (ev.key.repeat == 0) ToggleFullScreen(!_fullscreen);
554 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
557 if (keycode == WKC_DELETE ||
558 keycode == WKC_NUM_ENTER ||
559 keycode == WKC_LEFT ||
560 keycode == WKC_RIGHT ||
561 keycode & WKC_META ||
562 keycode & WKC_SHIFT ||
563 keycode & WKC_CTRL ||
565 (keycode >= WKC_F1 && keycode <= WKC_F12) ||
572 case SDL_TEXTINPUT: {
574 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
575 uint keycode = ConvertSdlKeycodeIntoMy(kc);
581 case SDL_WINDOWEVENT: {
582 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
584 _num_dirty_rects = MAX_DIRTY_RECTS + 1;
585 }
else if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
586 int w =
max(ev.window.data1, 64);
587 int h =
max(ev.window.data2, 64);
588 CreateMainSurface(w, h, w != ev.window.data1 || h != ev.window.data2);
589 }
else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
592 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
608 SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION ,
"0");
614 if (SDL_WasInit(SDL_INIT_VIDEO) == 0) {
615 ret_code = SDL_InitSubSystem(SDL_INIT_VIDEO);
617 if (ret_code < 0)
return SDL_GetError();
621 return SDL_GetError();
624 const char *dname = SDL_GetVideoDriver(0);
625 DEBUG(driver, 1,
"SDL2: using driver '%s'", dname);
636 SDL_QuitSubSystem(SDL_INIT_VIDEO);
637 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
644 uint32 cur_ticks = SDL_GetTicks();
645 uint32 last_cur_ticks = cur_ticks;
653 std::thread draw_thread;
654 std::unique_lock<std::recursive_mutex> draw_lock;
662 draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
674 _draw_mutex =
nullptr;
686 uint32 prev_cur_ticks = cur_ticks;
689 while (PollEvent() == -1) {}
690 if (_exit_game)
break;
692 mod = SDL_GetModState();
693 keys = SDL_GetKeyboardState(&numkeys);
700 if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
703 if (!
_networking && _game_mode != GM_MENU) _fast_forward |= 2;
704 }
else if (_fast_forward & 2) {
708 cur_ticks = SDL_GetTicks();
709 if (SDL_TICKS_PASSED(cur_ticks, next_tick) || (_fast_forward && !
_pause_mode) || cur_ticks < prev_cur_ticks) {
711 last_cur_ticks = cur_ticks;
721 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
722 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
723 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
724 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
729 if (_draw_mutex !=
nullptr) draw_lock.unlock();
733 if (_draw_mutex !=
nullptr) draw_lock.lock();
739 if (_draw_mutex !=
nullptr) draw_lock.unlock();
741 if (_draw_mutex !=
nullptr) draw_lock.lock();
753 DrawSurfaceToScreen();
757 if (_draw_mutex !=
nullptr) {
762 if (draw_lock.owns_lock()) draw_lock.unlock();
769 _draw_mutex =
nullptr;
776 std::unique_lock<std::recursive_mutex>
lock;
777 if (_draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
779 return CreateMainSurface(w, h,
true);
784 std::unique_lock<std::recursive_mutex>
lock;
785 if (_draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
789 SDL_GetWindowSize(_sdl_window, &_window_size_w, &_window_size_h);
793 if (SDL_GetCurrentDisplayMode(0, &dm) < 0) {
794 DEBUG(driver, 0,
"SDL_GetCurrentDisplayMode() failed: %s", SDL_GetError());
796 SDL_SetWindowSize(_sdl_window, dm.w, dm.h);
800 DEBUG(driver, 1,
"SDL2: Setting %s", fullscreen ?
"fullscreen" :
"windowed");
801 int ret = SDL_SetWindowFullscreen(_sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
804 _fullscreen = fullscreen;
805 if (!fullscreen) SDL_SetWindowSize(_sdl_window, _window_size_w, _window_size_h);
807 DEBUG(driver, 0,
"SDL_SetWindowFullscreen() failed: %s", SDL_GetError());
816 SDL_GetWindowSize(_sdl_window, &w, &h);
817 return CreateMainSurface(w, h,
false);
822 if (_draw_mutex !=
nullptr) _draw_mutex->lock();
827 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.
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.
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.
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.
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.