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 Rect r = {left, top, left + width, top + height};
42 this->
MakeDirty(0, 0, _screen.width, _screen.height);
45 static const Dimension default_resolutions[] = {
59 static void FindResolutions()
63 for (
int i = 0; i < SDL_GetNumDisplayModes(0); i++) {
65 SDL_GetDisplayMode(0, i, &mode);
67 if (mode.w < 640 || mode.h < 480)
continue;
74 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
80 static void GetAvailableVideoMode(uint *w, uint *h)
93 if (newdelta < delta) {
102 static uint FindStartupDisplay(uint startup_display)
104 int num_displays = SDL_GetNumVideoDisplays();
107 if (
IsInsideBS(startup_display, 0, num_displays))
return startup_display;
111 SDL_GetGlobalMouseState(&mx, &my);
112 for (
int display = 0; display < num_displays; ++display) {
114 if (SDL_GetDisplayBounds(display, &r) == 0 &&
IsInsideBS(mx, r.x, r.w) &&
IsInsideBS(my, r.y, r.h)) {
115 Debug(driver, 1,
"SDL2: Mouse is at ({}, {}), use display {} ({}, {}, {}, {})", mx, my, display, r.x, r.y, r.w, r.h);
139 flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
142 flags |= SDL_WINDOW_FULLSCREEN;
145 int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
147 if (SDL_GetDisplayBounds(this->startup_display, &r) == 0) {
148 x = r.x + std::max(0, r.w -
static_cast<int>(w)) / 2;
149 y = r.y + std::max(0, r.h -
static_cast<int>(h)) / 4;
153 seprintf(caption,
lastof(caption),
"OpenTTD %s", _openttd_revision);
161 Debug(driver, 0,
"SDL2: Couldn't allocate a window to draw on: {}", SDL_GetError());
166 if (!icon_path.empty()) {
168 SDL_Surface *icon = SDL_LoadBMP(icon_path.c_str());
169 if (icon !=
nullptr) {
171 uint32 rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
173 SDL_SetColorKey(icon, SDL_TRUE, rgbmap);
175 SDL_FreeSurface(icon);
182 bool VideoDriver_SDL_Base::CreateMainSurface(uint w, uint h,
bool resize)
184 GetAvailableVideoMode(&w, &h);
185 Debug(driver, 1,
"SDL2: using mode {}x{}", w, h);
188 if (resize) SDL_SetWindowSize(this->
sdl_window, w, h);
194 if (_fullscreen) _cursor.
in_window =
true;
199 bool VideoDriver_SDL_Base::ClaimMousePointer()
202 #ifndef __EMSCRIPTEN__
214 SDL_StartTextInput();
232 std::vector<int> rates = {};
233 for (
int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
234 SDL_DisplayMode mode = {};
235 if (SDL_GetDisplayMode(i, 0, &mode) != 0)
continue;
236 if (mode.refresh_rate != 0) rates.push_back(mode.refresh_rate);
249 #define AS(x, z) {x, 0, z, false}
250 #define AM(x, y, z, w) {x, (byte)(y - x), z, false}
251 #define AS_UP(x, z) {x, 0, z, true}
252 #define AM_UP(x, y, z, w) {x, (byte)(y - x), z, true}
256 AS_UP(SDLK_PAGEUP, WKC_PAGEUP),
257 AS_UP(SDLK_PAGEDOWN, WKC_PAGEDOWN),
258 AS_UP(SDLK_UP, WKC_UP),
259 AS_UP(SDLK_DOWN, WKC_DOWN),
260 AS_UP(SDLK_LEFT, WKC_LEFT),
261 AS_UP(SDLK_RIGHT, WKC_RIGHT),
263 AS_UP(SDLK_HOME, WKC_HOME),
264 AS_UP(SDLK_END, WKC_END),
266 AS_UP(SDLK_INSERT, WKC_INSERT),
267 AS_UP(SDLK_DELETE, WKC_DELETE),
270 AM(SDLK_a, SDLK_z,
'A',
'Z'),
271 AM(SDLK_0, SDLK_9,
'0',
'9'),
273 AS_UP(SDLK_ESCAPE, WKC_ESC),
274 AS_UP(SDLK_PAUSE, WKC_PAUSE),
275 AS_UP(SDLK_BACKSPACE, WKC_BACKSPACE),
277 AS(SDLK_SPACE, WKC_SPACE),
278 AS(SDLK_RETURN, WKC_RETURN),
279 AS(SDLK_TAB, WKC_TAB),
282 AM_UP(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
285 AM(SDLK_KP_0, SDLK_KP_9,
'0',
'9'),
286 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
287 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
288 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
289 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
290 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
291 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
307 static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym,
WChar *character)
311 bool unprintable =
false;
313 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
314 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
315 key = sym->sym - map->vk_from + map->map_to;
316 unprintable = map->unprintable;
322 if (sym->scancode == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
325 if (sym->mod & KMOD_GUI) key |= WKC_META;
326 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
327 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
328 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
331 if (sym->mod & KMOD_GUI ||
332 sym->mod & KMOD_CTRL ||
333 sym->mod & KMOD_ALT ||
335 *character = WKC_NONE;
337 *character = sym->sym;
352 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
353 if ((uint)(kc - map->vk_from) <= map->vk_count) {
354 key = kc - map->vk_from + map->map_to;
361 SDL_Scancode sc = SDL_GetScancodeFromKey(kc);
362 if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
371 if (!SDL_PollEvent(&ev))
return false;
374 case SDL_MOUSEMOTION:
382 if (ev.wheel.y > 0) {
384 }
else if (ev.wheel.y < 0) {
389 case SDL_MOUSEBUTTONDOWN:
391 ev.button.button = SDL_BUTTON_RIGHT;
394 switch (ev.button.button) {
395 case SDL_BUTTON_LEFT:
399 case SDL_BUTTON_RIGHT:
409 case SDL_MOUSEBUTTONUP:
414 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
417 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
424 HandleExitGameRequest();
428 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_GUI)) &&
429 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
430 if (ev.key.repeat == 0) ToggleFullScreen(!_fullscreen);
434 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
438 keycode == WKC_DELETE ||
439 keycode == WKC_NUM_ENTER ||
440 keycode == WKC_LEFT ||
441 keycode == WKC_RIGHT ||
443 keycode == WKC_DOWN ||
444 keycode == WKC_HOME ||
445 keycode == WKC_END ||
446 keycode & WKC_META ||
447 keycode & WKC_CTRL ||
449 (keycode >= WKC_F1 && keycode <= WKC_F12) ||
456 case SDL_TEXTINPUT: {
458 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
470 case SDL_WINDOWEVENT: {
471 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
473 this->
MakeDirty(0, 0, _screen.width, _screen.height);
474 }
else if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
475 int w = std::max(ev.window.data1, 64);
476 int h = std::max(ev.window.data2, 64);
477 CreateMainSurface(w, h, w != ev.window.data1 || h != ev.window.data2);
478 }
else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
481 #ifdef __EMSCRIPTEN__
483 SDL_SetRelativeMouseMode(SDL_FALSE);
485 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
497 static const char *InitializeSDL()
502 SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION,
"0");
503 #ifndef __EMSCRIPTEN__
504 SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP,
"1");
508 if (SDL_WasInit(SDL_INIT_VIDEO) != 0)
return nullptr;
510 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
return SDL_GetError();
514 const char *VideoDriver_SDL_Base::Initialize()
518 const char *
error = InitializeSDL();
531 const char *
error = this->Initialize();
534 this->startup_display = FindStartupDisplay(
GetDriverParamInt(param,
"display", -1));
537 return SDL_GetError();
540 const char *dname = SDL_GetCurrentVideoDriver();
541 Debug(driver, 1,
"SDL2: using driver '{}'", dname);
548 #ifdef __EMSCRIPTEN__
549 this->is_game_threaded =
false;
559 SDL_QuitSubSystem(SDL_INIT_VIDEO);
560 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
567 uint32 mod = SDL_GetModState();
568 const Uint8 *keys = SDL_GetKeyboardState(
nullptr);
585 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
586 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
587 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
588 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
593 void VideoDriver_SDL_Base::LoopOnce()
596 #ifdef __EMSCRIPTEN__
603 emscripten_cancel_main_loop();
604 emscripten_exit_pointerlock();
608 EM_ASM(
if (window[
"openttd_syncfs"]) openttd_syncfs());
609 EM_ASM(
if (window[
"openttd_exit"]) openttd_exit());
618 #ifndef __EMSCRIPTEN__
625 #ifdef __EMSCRIPTEN__
627 emscripten_set_main_loop_arg(&this->EmscriptenLoop,
this, 0, 1);
631 while (!_exit_game) {
641 return CreateMainSurface(w, h,
true);
654 if (SDL_GetCurrentDisplayMode(0, &dm) < 0) {
655 Debug(driver, 0,
"SDL_GetCurrentDisplayMode() failed: {}", SDL_GetError());
657 SDL_SetWindowSize(this->
sdl_window, dm.w, dm.h);
661 Debug(driver, 1,
"SDL2: Setting {}", fullscreen ?
"fullscreen" :
"windowed");
662 int ret = SDL_SetWindowFullscreen(this->
sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
665 _fullscreen = fullscreen;
666 if (!fullscreen) SDL_SetWindowSize(this->
sdl_window, w, h);
668 Debug(driver, 0,
"SDL_SetWindowFullscreen() failed: {}", SDL_GetError());
680 return CreateMainSurface(w, h,
false);
685 SDL_DisplayMode mode;
688 return {
static_cast<uint
>(mode.w),
static_cast<uint
>(mode.h) };
697 assert(_screen.dst_ptr !=
nullptr);
704 if (_screen.dst_ptr !=
nullptr) {
707 _screen.dst_ptr =
nullptr;