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"
27 # include <emscripten.h>
28 # include <emscripten/html5.h>
31 #include "../safeguards.h"
35 static bool _cursor_new_in_window =
false;
40 Rect r = {left, top, left + width, top + height};
49 this->
MakeDirty(0, 0, _screen.width, _screen.height);
52 static const Dimension default_resolutions[] = {
66 static void FindResolutions()
70 for (
int i = 0; i < SDL_GetNumDisplayModes(0); i++) {
72 SDL_GetDisplayMode(0, i, &mode);
74 if (mode.w < 640 || mode.h < 480)
continue;
81 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
87 static void GetAvailableVideoMode(uint *w, uint *h)
100 if (newdelta < delta) {
109 static uint FindStartupDisplay(uint startup_display)
111 int num_displays = SDL_GetNumVideoDisplays();
114 if (
IsInsideBS(startup_display, 0, num_displays))
return startup_display;
118 SDL_GetGlobalMouseState(&mx, &my);
119 for (
int display = 0; display < num_displays; ++display) {
121 if (SDL_GetDisplayBounds(display, &r) == 0 &&
IsInsideBS(mx, r.x, r.w) &&
IsInsideBS(my, r.y, r.h)) {
122 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);
149 flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
152 flags |= SDL_WINDOW_FULLSCREEN;
155 int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
157 if (SDL_GetDisplayBounds(this->startup_display, &r) == 0) {
158 x = r.x + std::max(0, r.w -
static_cast<int>(w)) / 2;
159 y = r.y + std::max(0, r.h -
static_cast<int>(h)) / 4;
163 seprintf(caption,
lastof(caption),
"OpenTTD %s", _openttd_revision);
171 DEBUG(driver, 0,
"SDL2: Couldn't allocate a window to draw on: %s", SDL_GetError());
176 if (!icon_path.empty()) {
178 SDL_Surface *icon = SDL_LoadBMP(icon_path.c_str());
179 if (icon !=
nullptr) {
181 uint32 rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
183 SDL_SetColorKey(icon, SDL_TRUE, rgbmap);
185 SDL_FreeSurface(icon);
192 bool VideoDriver_SDL_Base::CreateMainSurface(uint w, uint h,
bool resize)
194 GetAvailableVideoMode(&w, &h);
195 DEBUG(driver, 1,
"SDL2: using mode %ux%u", w, h);
198 if (resize) SDL_SetWindowSize(this->
sdl_window, w, h);
204 if (_fullscreen) _cursor.
in_window =
true;
209 bool VideoDriver_SDL_Base::ClaimMousePointer()
212 #ifdef __EMSCRIPTEN__
213 SDL_SetRelativeMouseMode(SDL_TRUE);
224 SDL_StartTextInput();
242 std::vector<int> rates = {};
243 for (
int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
244 SDL_DisplayMode mode = {};
245 if (SDL_GetDisplayMode(i, 0, &mode) != 0)
continue;
246 if (mode.refresh_rate != 0) rates.push_back(mode.refresh_rate);
259 #define AS(x, z) {x, 0, z, false}
260 #define AM(x, y, z, w) {x, (byte)(y - x), z, false}
261 #define AS_UP(x, z) {x, 0, z, true}
262 #define AM_UP(x, y, z, w) {x, (byte)(y - x), z, true}
266 AS_UP(SDLK_PAGEUP, WKC_PAGEUP),
267 AS_UP(SDLK_PAGEDOWN, WKC_PAGEDOWN),
268 AS_UP(SDLK_UP, WKC_UP),
269 AS_UP(SDLK_DOWN, WKC_DOWN),
270 AS_UP(SDLK_LEFT, WKC_LEFT),
271 AS_UP(SDLK_RIGHT, WKC_RIGHT),
273 AS_UP(SDLK_HOME, WKC_HOME),
274 AS_UP(SDLK_END, WKC_END),
276 AS_UP(SDLK_INSERT, WKC_INSERT),
277 AS_UP(SDLK_DELETE, WKC_DELETE),
280 AM(SDLK_a, SDLK_z,
'A',
'Z'),
281 AM(SDLK_0, SDLK_9,
'0',
'9'),
283 AS_UP(SDLK_ESCAPE, WKC_ESC),
284 AS_UP(SDLK_PAUSE, WKC_PAUSE),
285 AS_UP(SDLK_BACKSPACE, WKC_BACKSPACE),
287 AS(SDLK_SPACE, WKC_SPACE),
288 AS(SDLK_RETURN, WKC_RETURN),
289 AS(SDLK_TAB, WKC_TAB),
292 AM_UP(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
295 AM(SDLK_KP_0, SDLK_KP_9,
'0',
'9'),
296 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
297 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
298 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
299 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
300 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
301 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
317 static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym,
WChar *character)
321 bool unprintable =
false;
323 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
324 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
325 key = sym->sym - map->vk_from + map->map_to;
326 unprintable = map->unprintable;
332 if (sym->scancode == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
335 if (sym->mod & KMOD_GUI) key |= WKC_META;
336 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
337 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
338 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
341 if (sym->mod & KMOD_GUI ||
342 sym->mod & KMOD_CTRL ||
343 sym->mod & KMOD_ALT ||
345 *character = WKC_NONE;
347 *character = sym->sym;
362 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
363 if ((uint)(kc - map->vk_from) <= map->vk_count) {
364 key = kc - map->vk_from + map->map_to;
371 SDL_Scancode sc = SDL_GetScancodeFromKey(kc);
372 if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
381 if (!SDL_PollEvent(&ev))
return false;
384 case SDL_MOUSEMOTION:
385 #ifdef __EMSCRIPTEN__
386 if (_cursor_new_in_window) {
392 _cursor.
pos.x = ev.motion.x;
393 _cursor.
pos.y = ev.motion.y;
394 _cursor.
dirty =
true;
396 _cursor_new_in_window =
false;
397 SDL_SetRelativeMouseMode(SDL_TRUE);
410 if (ev.wheel.y > 0) {
412 }
else if (ev.wheel.y < 0) {
417 case SDL_MOUSEBUTTONDOWN:
419 ev.button.button = SDL_BUTTON_RIGHT;
422 switch (ev.button.button) {
423 case SDL_BUTTON_LEFT:
427 case SDL_BUTTON_RIGHT:
437 case SDL_MOUSEBUTTONUP:
442 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
445 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
452 HandleExitGameRequest();
456 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_GUI)) &&
457 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
458 if (ev.key.repeat == 0) ToggleFullScreen(!_fullscreen);
462 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
466 keycode == WKC_DELETE ||
467 keycode == WKC_NUM_ENTER ||
468 keycode == WKC_LEFT ||
469 keycode == WKC_RIGHT ||
471 keycode == WKC_DOWN ||
472 keycode == WKC_HOME ||
473 keycode == WKC_END ||
474 keycode & WKC_META ||
475 keycode & WKC_CTRL ||
477 (keycode >= WKC_F1 && keycode <= WKC_F12) ||
484 case SDL_TEXTINPUT: {
486 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
498 case SDL_WINDOWEVENT: {
499 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
501 this->
MakeDirty(0, 0, _screen.width, _screen.height);
502 }
else if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
503 int w = std::max(ev.window.data1, 64);
504 int h = std::max(ev.window.data2, 64);
505 CreateMainSurface(w, h, w != ev.window.data1 || h != ev.window.data2);
506 }
else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
509 #ifdef __EMSCRIPTEN__
512 _cursor_new_in_window =
true;
513 SDL_SetRelativeMouseMode(SDL_FALSE);
515 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
527 static const char *InitializeSDL()
532 SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION,
"0");
533 SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP,
"1");
536 if (SDL_WasInit(SDL_INIT_VIDEO) != 0)
return nullptr;
538 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
return SDL_GetError();
542 const char *VideoDriver_SDL_Base::Initialize()
546 const char *
error = InitializeSDL();
559 const char *
error = this->Initialize();
562 this->startup_display = FindStartupDisplay(
GetDriverParamInt(param,
"display", -1));
565 return SDL_GetError();
568 const char *dname = SDL_GetCurrentVideoDriver();
569 DEBUG(driver, 1,
"SDL2: using driver '%s'", dname);
576 #ifdef __EMSCRIPTEN__
577 this->is_game_threaded =
false;
587 SDL_QuitSubSystem(SDL_INIT_VIDEO);
588 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
595 uint32 mod = SDL_GetModState();
596 const Uint8 *keys = SDL_GetKeyboardState(NULL);
613 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
614 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
615 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
616 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
621 void VideoDriver_SDL_Base::LoopOnce()
624 #ifdef __EMSCRIPTEN__
631 emscripten_cancel_main_loop();
632 emscripten_exit_pointerlock();
636 EM_ASM(
if (window[
"openttd_syncfs"]) openttd_syncfs());
637 EM_ASM(
if (window[
"openttd_exit"]) openttd_exit());
646 #ifndef __EMSCRIPTEN__
653 #ifdef __EMSCRIPTEN__
655 emscripten_set_main_loop_arg(&this->EmscriptenLoop,
this, 0, 1);
659 while (!_exit_game) {
669 return CreateMainSurface(w, h,
true);
682 if (SDL_GetCurrentDisplayMode(0, &dm) < 0) {
683 DEBUG(driver, 0,
"SDL_GetCurrentDisplayMode() failed: %s", SDL_GetError());
685 SDL_SetWindowSize(this->
sdl_window, dm.w, dm.h);
689 DEBUG(driver, 1,
"SDL2: Setting %s", fullscreen ?
"fullscreen" :
"windowed");
690 int ret = SDL_SetWindowFullscreen(this->
sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
693 _fullscreen = fullscreen;
694 if (!fullscreen) SDL_SetWindowSize(this->
sdl_window, w, h);
696 DEBUG(driver, 0,
"SDL_SetWindowFullscreen() failed: %s", SDL_GetError());
708 return CreateMainSurface(w, h,
false);
713 SDL_DisplayMode mode;
716 return {
static_cast<uint
>(mode.w),
static_cast<uint
>(mode.h) };
725 assert(_screen.dst_ptr !=
nullptr);
732 if (_screen.dst_ptr !=
nullptr) {
735 _screen.dst_ptr =
nullptr;