10 #include "../stdafx.h"
11 #include "../openttd.h"
12 #include "../gfx_func.h"
14 #include "../blitter/factory.hpp"
15 #include "../thread.h"
16 #include "../progress.h"
17 #include "../core/random_func.hpp"
18 #include "../core/math_func.hpp"
19 #include "../core/mem_func.hpp"
20 #include "../core/geometry_func.hpp"
21 #include "../fileio_func.h"
22 #include "../framerate_type.h"
23 #include "../window_func.h"
28 # include <emscripten.h>
29 # include <emscripten/html5.h>
32 #include "../safeguards.h"
36 static bool _cursor_new_in_window =
false;
41 Rect r = {left, top, left + width, top + height};
50 this->
MakeDirty(0, 0, _screen.width, _screen.height);
58 static const Dimension default_resolutions[] = {
72 static void FindResolutions()
76 for (
int i = 0; i < SDL_GetNumDisplayModes(0); i++) {
78 SDL_GetDisplayMode(0, i, &mode);
80 if (mode.w < 640 || mode.h < 480)
continue;
87 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
93 static void GetAvailableVideoMode(uint *w, uint *h)
106 if (newdelta < delta) {
115 static uint FindStartupDisplay(uint startup_display)
117 int num_displays = SDL_GetNumVideoDisplays();
120 if (
IsInsideBS(startup_display, 0, num_displays))
return startup_display;
124 SDL_GetGlobalMouseState(&mx, &my);
125 for (
int display = 0; display < num_displays; ++display) {
127 if (SDL_GetDisplayBounds(display, &r) == 0 &&
IsInsideBS(mx, r.x, r.w) &&
IsInsideBS(my, r.y, r.h)) {
128 DEBUG(driver, 1,
"SDL2: Mouse is at (%d, %d), use display %d (%d, %d, %d, %d)", mx, my, display, r.x, r.y, r.w, r.h);
155 flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
158 flags |= SDL_WINDOW_FULLSCREEN;
161 int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
163 if (SDL_GetDisplayBounds(this->startup_display, &r) == 0) {
164 x = r.x + std::max(0, r.w -
static_cast<int>(w)) / 2;
165 y = r.y + std::max(0, r.h -
static_cast<int>(h)) / 4;
169 seprintf(caption,
lastof(caption),
"OpenTTD %s", _openttd_revision);
177 DEBUG(driver, 0,
"SDL2: Couldn't allocate a window to draw on: %s", SDL_GetError());
182 if (!icon_path.empty()) {
184 SDL_Surface *icon = SDL_LoadBMP(icon_path.c_str());
185 if (icon !=
nullptr) {
187 uint32 rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
189 SDL_SetColorKey(icon, SDL_TRUE, rgbmap);
191 SDL_FreeSurface(icon);
198 bool VideoDriver_SDL_Base::CreateMainSurface(uint w, uint h,
bool resize)
200 GetAvailableVideoMode(&w, &h);
201 DEBUG(driver, 1,
"SDL2: using mode %ux%u", w, h);
204 if (resize) SDL_SetWindowSize(this->
sdl_window, w, h);
210 if (_fullscreen) _cursor.
in_window =
true;
215 bool VideoDriver_SDL_Base::ClaimMousePointer()
218 #ifdef __EMSCRIPTEN__
219 SDL_SetRelativeMouseMode(SDL_TRUE);
230 SDL_StartTextInput();
254 #define AS(x, z) {x, 0, z, false}
255 #define AM(x, y, z, w) {x, (byte)(y - x), z, false}
256 #define AS_UP(x, z) {x, 0, z, true}
257 #define AM_UP(x, y, z, w) {x, (byte)(y - x), z, true}
261 AS_UP(SDLK_PAGEUP, WKC_PAGEUP),
262 AS_UP(SDLK_PAGEDOWN, WKC_PAGEDOWN),
263 AS_UP(SDLK_UP, WKC_UP),
264 AS_UP(SDLK_DOWN, WKC_DOWN),
265 AS_UP(SDLK_LEFT, WKC_LEFT),
266 AS_UP(SDLK_RIGHT, WKC_RIGHT),
268 AS_UP(SDLK_HOME, WKC_HOME),
269 AS_UP(SDLK_END, WKC_END),
271 AS_UP(SDLK_INSERT, WKC_INSERT),
272 AS_UP(SDLK_DELETE, WKC_DELETE),
275 AM(SDLK_a, SDLK_z,
'A',
'Z'),
276 AM(SDLK_0, SDLK_9,
'0',
'9'),
278 AS_UP(SDLK_ESCAPE, WKC_ESC),
279 AS_UP(SDLK_PAUSE, WKC_PAUSE),
280 AS_UP(SDLK_BACKSPACE, WKC_BACKSPACE),
282 AS(SDLK_SPACE, WKC_SPACE),
283 AS(SDLK_RETURN, WKC_RETURN),
284 AS(SDLK_TAB, WKC_TAB),
287 AM_UP(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
290 AM(SDLK_KP_0, SDLK_KP_9,
'0',
'9'),
291 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
292 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
293 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
294 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
295 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
296 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
312 static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym,
WChar *character)
316 bool unprintable =
false;
318 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
319 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
320 key = sym->sym - map->vk_from + map->map_to;
321 unprintable = map->unprintable;
327 if (sym->scancode == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
330 if (sym->mod & KMOD_GUI) key |= WKC_META;
331 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
332 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
333 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
336 if (sym->mod & KMOD_GUI ||
337 sym->mod & KMOD_CTRL ||
338 sym->mod & KMOD_ALT ||
340 *character = WKC_NONE;
342 *character = sym->sym;
357 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
358 if ((uint)(kc - map->vk_from) <= map->vk_count) {
359 key = kc - map->vk_from + map->map_to;
366 SDL_Scancode sc = SDL_GetScancodeFromKey(kc);
367 if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
376 if (!SDL_PollEvent(&ev))
return false;
379 case SDL_MOUSEMOTION:
380 #ifdef __EMSCRIPTEN__
381 if (_cursor_new_in_window) {
387 _cursor.
pos.x = ev.motion.x;
388 _cursor.
pos.y = ev.motion.y;
389 _cursor.
dirty =
true;
391 _cursor_new_in_window =
false;
392 SDL_SetRelativeMouseMode(SDL_TRUE);
405 if (ev.wheel.y > 0) {
407 }
else if (ev.wheel.y < 0) {
412 case SDL_MOUSEBUTTONDOWN:
414 ev.button.button = SDL_BUTTON_RIGHT;
417 switch (ev.button.button) {
418 case SDL_BUTTON_LEFT:
422 case SDL_BUTTON_RIGHT:
432 case SDL_MOUSEBUTTONUP:
437 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
440 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
447 HandleExitGameRequest();
451 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_GUI)) &&
452 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
453 if (ev.key.repeat == 0) ToggleFullScreen(!_fullscreen);
457 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
461 keycode == WKC_DELETE ||
462 keycode == WKC_NUM_ENTER ||
463 keycode == WKC_LEFT ||
464 keycode == WKC_RIGHT ||
466 keycode == WKC_DOWN ||
467 keycode == WKC_HOME ||
468 keycode == WKC_END ||
469 keycode & WKC_META ||
470 keycode & WKC_CTRL ||
472 (keycode >= WKC_F1 && keycode <= WKC_F12) ||
479 case SDL_TEXTINPUT: {
481 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
493 case SDL_WINDOWEVENT: {
494 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
496 this->
MakeDirty(0, 0, _screen.width, _screen.height);
497 }
else if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
498 int w = std::max(ev.window.data1, 64);
499 int h = std::max(ev.window.data2, 64);
500 CreateMainSurface(w, h, w != ev.window.data1 || h != ev.window.data2);
501 }
else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
504 #ifdef __EMSCRIPTEN__
507 _cursor_new_in_window =
true;
508 SDL_SetRelativeMouseMode(SDL_FALSE);
510 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
522 static const char *InitializeSDL()
527 SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION,
"0");
528 SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP,
"1");
531 if (SDL_WasInit(SDL_INIT_VIDEO) != 0)
return nullptr;
533 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
return SDL_GetError();
537 const char *VideoDriver_SDL_Base::Initialize()
541 const char *
error = InitializeSDL();
554 const char *
error = this->Initialize();
557 this->startup_display = FindStartupDisplay(
GetDriverParamInt(parm,
"display", -1));
560 return SDL_GetError();
563 const char *dname = SDL_GetCurrentVideoDriver();
564 DEBUG(driver, 1,
"SDL2: using driver '%s'", dname);
577 if (strcmp(dname,
"wayland") == 0) {
589 SDL_QuitSubSystem(SDL_INIT_VIDEO);
590 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
597 uint32 mod = SDL_GetModState();
598 const Uint8 *keys = SDL_GetKeyboardState(NULL);
615 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
616 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
617 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
618 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
623 void VideoDriver_SDL_Base::LoopOnce()
626 #ifdef __EMSCRIPTEN__
633 emscripten_cancel_main_loop();
649 #ifndef __EMSCRIPTEN__
659 this->
draw_mutex =
new std::recursive_mutex();
663 draw_lock = std::unique_lock<std::recursive_mutex>(*this->
draw_mutex);
664 this->
draw_signal =
new std::condition_variable_any();
686 #ifdef __EMSCRIPTEN__
688 emscripten_set_main_loop_arg(&this->EmscriptenLoop,
this, 0, 1);
690 while (!_exit_game) {
698 void VideoDriver_SDL_Base::MainLoopCleanup()
705 if (draw_lock.owns_lock()) draw_lock.unlock();
716 #ifdef __EMSCRIPTEN__
717 emscripten_exit_pointerlock();
721 EM_ASM(
if (window[
"openttd_syncfs"]) openttd_syncfs());
722 EM_ASM(
if (window[
"openttd_exit"]) openttd_exit());
728 std::unique_lock<std::recursive_mutex>
lock;
729 if (this->
draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->
draw_mutex);
731 return CreateMainSurface(w, h,
true);
736 std::unique_lock<std::recursive_mutex>
lock;
737 if (this->
draw_mutex !=
nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->
draw_mutex);
747 if (SDL_GetCurrentDisplayMode(0, &dm) < 0) {
748 DEBUG(driver, 0,
"SDL_GetCurrentDisplayMode() failed: %s", SDL_GetError());
750 SDL_SetWindowSize(this->
sdl_window, dm.w, dm.h);
754 DEBUG(driver, 1,
"SDL2: Setting %s", fullscreen ?
"fullscreen" :
"windowed");
755 int ret = SDL_SetWindowFullscreen(this->
sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
758 _fullscreen = fullscreen;
759 if (!fullscreen) SDL_SetWindowSize(this->
sdl_window, w, h);
761 DEBUG(driver, 0,
"SDL_SetWindowFullscreen() failed: %s", SDL_GetError());
772 return CreateMainSurface(w, h,
false);
787 SDL_DisplayMode mode;
790 return {
static_cast<uint
>(mode.w),
static_cast<uint
>(mode.h) };
801 assert(_screen.dst_ptr !=
nullptr);
808 if (_screen.dst_ptr !=
nullptr) {
811 _screen.dst_ptr =
nullptr;