10 #include "../stdafx.h"
11 #include "../openttd.h"
12 #include "../gfx_func.h"
13 #include "../blitter/factory.hpp"
14 #include "../thread.h"
15 #include "../progress.h"
16 #include "../core/random_func.hpp"
17 #include "../core/math_func.hpp"
18 #include "../core/mem_func.hpp"
19 #include "../core/geometry_func.hpp"
20 #include "../fileio_func.h"
21 #include "../framerate_type.h"
22 #include "../window_func.h"
26 # include <emscripten.h>
27 # include <emscripten/html5.h>
30 #include "../safeguards.h"
34 Rect r = {left, top, left + width, top + height};
41 this->
MakeDirty(0, 0, _screen.width, _screen.height);
44 static const Dimension default_resolutions[] = {
58 static void FindResolutions()
62 for (
int display = 0; display < SDL_GetNumVideoDisplays(); display++) {
63 for (
int i = 0; i < SDL_GetNumDisplayModes(display); i++) {
65 SDL_GetDisplayMode(display, i, &mode);
67 if (mode.w < 640 || mode.h < 480)
continue;
75 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
81 static void GetAvailableVideoMode(uint *w, uint *h)
94 if (newdelta < delta) {
103 static uint FindStartupDisplay(uint startup_display)
105 int num_displays = SDL_GetNumVideoDisplays();
108 if (
IsInsideBS(startup_display, 0, num_displays))
return startup_display;
112 SDL_GetGlobalMouseState(&mx, &my);
113 for (
int display = 0; display < num_displays; ++display) {
115 if (SDL_GetDisplayBounds(display, &r) == 0 &&
IsInsideBS(mx, r.x, r.w) &&
IsInsideBS(my, r.y, r.h)) {
116 Debug(driver, 1,
"SDL2: Mouse is at ({}, {}), use display {} ({}, {}, {}, {})", mx, my, display, r.x, r.y, r.w, r.h);
140 flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
143 flags |= SDL_WINDOW_FULLSCREEN;
146 int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
148 if (SDL_GetDisplayBounds(this->startup_display, &r) == 0) {
149 x = r.x + std::max(0, r.w -
static_cast<int>(w)) / 2;
150 y = r.y + std::max(0, r.h -
static_cast<int>(h)) / 4;
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_t 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, char32_t *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: {
375 int32_t x = ev.motion.x;
376 int32_t y = ev.motion.y;
381 while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) {
395 if (ev.wheel.y > 0) {
397 }
else if (ev.wheel.y < 0) {
402 case SDL_MOUSEBUTTONDOWN:
404 ev.button.button = SDL_BUTTON_RIGHT;
407 switch (ev.button.button) {
408 case SDL_BUTTON_LEFT:
412 case SDL_BUTTON_RIGHT:
422 case SDL_MOUSEBUTTONUP:
427 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
430 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
437 HandleExitGameRequest();
441 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_GUI)) &&
442 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
443 if (ev.key.repeat == 0) ToggleFullScreen(!_fullscreen);
447 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
451 keycode == WKC_DELETE ||
452 keycode == WKC_NUM_ENTER ||
453 keycode == WKC_LEFT ||
454 keycode == WKC_RIGHT ||
456 keycode == WKC_DOWN ||
457 keycode == WKC_HOME ||
458 keycode == WKC_END ||
459 keycode & WKC_META ||
460 keycode & WKC_CTRL ||
462 (keycode >= WKC_F1 && keycode <= WKC_F12) ||
469 case SDL_TEXTINPUT: {
471 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
483 case SDL_WINDOWEVENT: {
484 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
486 this->
MakeDirty(0, 0, _screen.width, _screen.height);
487 }
else if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
488 int w = std::max(ev.window.data1, 64);
489 int h = std::max(ev.window.data2, 64);
490 CreateMainSurface(w, h, w != ev.window.data1 || h != ev.window.data2);
491 }
else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
495 SDL_SetRelativeMouseMode(SDL_FALSE);
496 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
508 static const char *InitializeSDL()
511 if (SDL_WasInit(SDL_INIT_VIDEO) != 0)
return nullptr;
513 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
return SDL_GetError();
517 const char *VideoDriver_SDL_Base::Initialize()
521 const char *error = InitializeSDL();
522 if (error !=
nullptr)
return error;
534 const char *error = this->Initialize();
535 if (error !=
nullptr)
return error;
537 this->startup_display = FindStartupDisplay(
GetDriverParamInt(param,
"display", -1));
540 return SDL_GetError();
543 const char *dname = SDL_GetCurrentVideoDriver();
544 Debug(driver, 1,
"SDL2: using driver '{}'", dname);
556 #ifdef __EMSCRIPTEN__
557 this->is_game_threaded =
false;
567 SDL_QuitSubSystem(SDL_INIT_VIDEO);
568 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
575 uint32_t mod = SDL_GetModState();
576 const Uint8 *keys = SDL_GetKeyboardState(
nullptr);
589 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
590 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
591 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
592 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
597 void VideoDriver_SDL_Base::LoopOnce()
600 #ifdef __EMSCRIPTEN__
607 extern void PostMainLoop();
610 emscripten_cancel_main_loop();
611 emscripten_exit_pointerlock();
615 if (_game_mode == GM_BOOTSTRAP) {
616 EM_ASM(
if (window[
"openttd_bootstrap_reload"]) openttd_bootstrap_reload());
618 EM_ASM(
if (window[
"openttd_exit"]) openttd_exit());
628 #ifndef __EMSCRIPTEN__
635 #ifdef __EMSCRIPTEN__
637 emscripten_set_main_loop_arg(&this->EmscriptenLoop,
this, 0, 1);
641 while (!_exit_game) {
651 return CreateMainSurface(w, h,
true);
663 if (SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(this->
sdl_window), &dm) < 0) {
664 Debug(driver, 0,
"SDL_GetCurrentDisplayMode() failed: {}", SDL_GetError());
666 SDL_SetWindowSize(this->
sdl_window, dm.w, dm.h);
670 Debug(driver, 1,
"SDL2: Setting {}", fullscreen ?
"fullscreen" :
"windowed");
671 int ret = SDL_SetWindowFullscreen(this->
sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
674 _fullscreen = fullscreen;
675 if (!fullscreen) SDL_SetWindowSize(this->
sdl_window, w, h);
677 Debug(driver, 0,
"SDL_SetWindowFullscreen() failed: {}", SDL_GetError());
689 return CreateMainSurface(w, h,
false);
694 SDL_DisplayMode mode;
697 return {
static_cast<uint
>(mode.w),
static_cast<uint
>(mode.h) };
706 assert(_screen.dst_ptr !=
nullptr);
713 if (_screen.dst_ptr !=
nullptr) {
716 _screen.dst_ptr =
nullptr;