25# include <emscripten.h>
26# include <emscripten/html5.h>
33 Rect r = {left, top, left + width, top + height};
40 this->
MakeDirty(0, 0, _screen.width, _screen.height);
63 for (
int display = 0; display < SDL_GetNumVideoDisplays(); display++) {
64 for (
int i = 0; i < SDL_GetNumDisplayModes(display); i++) {
66 SDL_GetDisplayMode(display, i, &mode);
68 if (mode.w < 640 || mode.h < 480)
continue;
100 if (newdelta < delta) {
116 int num_displays = SDL_GetNumVideoDisplays();
119 if (
IsInsideBS(startup_display, 0, num_displays))
return startup_display;
123 SDL_GetGlobalMouseState(&mx, &my);
124 for (
int display = 0; display < num_displays; ++display) {
126 if (SDL_GetDisplayBounds(display, &r) == 0 &&
IsInsideBS(mx, r.x, r.w) &&
IsInsideBS(my, r.y, r.h)) {
127 Debug(driver, 1,
"SDL2: Mouse is at ({}, {}), use display {} ({}, {}, {}, {})", mx, my, display, r.x, r.y, r.w, r.h);
157 flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
160 flags |= SDL_WINDOW_FULLSCREEN;
163 int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
166 x = r.x + std::max(0, r.w -
static_cast<int>(w)) / 2;
167 y = r.y + std::max(0, r.h -
static_cast<int>(h)) / 4;
178 Debug(driver, 0,
"SDL2: Couldn't allocate a window to draw on: {}", SDL_GetError());
183 if (!icon_path.empty()) {
185 SDL_Surface *icon = SDL_LoadBMP(icon_path.c_str());
186 if (icon !=
nullptr) {
188 uint32_t rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
190 SDL_SetColorKey(icon, SDL_TRUE, rgbmap);
192 SDL_FreeSurface(icon);
209 Debug(driver, 1,
"SDL2: using mode {}x{}", w, h);
212 if (resize) SDL_SetWindowSize(this->
sdl_window, w, h);
218 if (_fullscreen) _cursor.in_window =
true;
226#ifndef __EMSCRIPTEN__
237 SDL_StartTextInput();
255 std::vector<int> rates = {};
256 for (
int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
257 SDL_DisplayMode mode = {};
258 if (SDL_GetDisplayMode(i, 0, &mode) != 0)
continue;
259 if (mode.refresh_rate != 0) rates.push_back(mode.refresh_rate);
283 assert(vk_first < vk_last);
284 assert(map_first < map_last);
285 assert(this->count ==
static_cast<uint
>(map_last - map_first + 1));
301 { SDLK_PAGEUP, WKC_PAGEUP,
true },
302 { SDLK_PAGEDOWN, WKC_PAGEDOWN,
true },
303 { SDLK_UP, WKC_UP,
true },
304 { SDLK_DOWN, WKC_DOWN,
true },
305 { SDLK_LEFT, WKC_LEFT,
true },
306 { SDLK_RIGHT, WKC_RIGHT,
true },
308 { SDLK_HOME, WKC_HOME,
true },
309 { SDLK_END, WKC_END,
true },
311 { SDLK_INSERT, WKC_INSERT,
true },
312 { SDLK_DELETE, WKC_DELETE,
true },
314 { SDLK_ESCAPE, WKC_ESC,
true },
315 { SDLK_PAUSE, WKC_PAUSE,
true },
316 { SDLK_BACKSPACE, WKC_BACKSPACE,
true },
318 { SDLK_F1, SDLK_F12, WKC_F1, WKC_F12,
true },
324 { SDLK_SPACE, WKC_SPACE,
false },
325 { SDLK_RETURN, WKC_RETURN,
false },
326 { SDLK_TAB, WKC_TAB,
false },
331 { SDLK_KP_DIVIDE, WKC_NUM_DIV,
false },
332 { SDLK_KP_MULTIPLY, WKC_NUM_MUL,
false },
333 { SDLK_KP_MINUS, WKC_NUM_MINUS,
false },
334 { SDLK_KP_PLUS, WKC_NUM_PLUS,
false },
335 { SDLK_KP_ENTER, WKC_NUM_ENTER,
false },
336 { SDLK_KP_PERIOD, WKC_NUM_DECIMAL,
false },
361 bool unprintable =
false;
363 for (
const auto &map : _vk_mapping) {
364 if (
IsInsideBS(sym->sym, map.vk_from, map.count)) {
365 key = sym->sym - map.vk_from + map.map_to;
366 unprintable = map.unprintable;
372 if (sym->scancode == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
375 if (sym->mod & KMOD_GUI) key |= WKC_META;
376 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
377 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
378 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
381 if (sym->mod & KMOD_GUI ||
382 sym->mod & KMOD_CTRL ||
383 sym->mod & KMOD_ALT ||
387 *character = sym->sym;
402 for (
const auto &map : _vk_mapping) {
404 key = kc - map.vk_from + map.map_to;
411 SDL_Scancode sc = SDL_GetScancodeFromKey(kc);
412 if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
421 if (!SDL_PollEvent(&ev))
return false;
424 case SDL_MOUSEMOTION: {
425 int32_t x = ev.motion.x;
426 int32_t y = ev.motion.y;
428 if (_cursor.fix_at) {
431 while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) {
437 if (_cursor.UpdateCursorPosition(x, y)) {
438 SDL_WarpMouseInWindow(this->
sdl_window, _cursor.pos.x, _cursor.pos.y);
444 case SDL_MOUSEWHEEL: {
445 if (ev.wheel.y > 0) {
447 }
else if (ev.wheel.y < 0) {
452 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f;
453#if SDL_VERSION_ATLEAST(2, 18, 0)
454 _cursor.v_wheel -= ev.wheel.preciseY * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
455 _cursor.h_wheel += ev.wheel.preciseX * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
457 _cursor.v_wheel -=
static_cast<float>(ev.wheel.y * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier);
458 _cursor.h_wheel +=
static_cast<float>(ev.wheel.x * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier);
460 _cursor.wheel_moved =
true;
464 case SDL_MOUSEBUTTONDOWN:
466 ev.button.button = SDL_BUTTON_RIGHT;
469 switch (ev.button.button) {
470 case SDL_BUTTON_LEFT:
474 case SDL_BUTTON_RIGHT:
484 case SDL_MOUSEBUTTONUP:
489 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
492 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
499 HandleExitGameRequest();
503 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_GUI)) &&
504 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
505 if (ev.key.repeat == 0) ToggleFullScreen(!_fullscreen);
513 keycode == WKC_DELETE ||
514 keycode == WKC_NUM_ENTER ||
515 keycode == WKC_LEFT ||
516 keycode == WKC_RIGHT ||
518 keycode == WKC_DOWN ||
519 keycode == WKC_HOME ||
520 keycode == WKC_END ||
521 keycode & WKC_META ||
522 keycode & WKC_CTRL ||
524 (keycode >= WKC_F1 && keycode <= WKC_F12) ||
531 case SDL_TEXTINPUT: {
533 SDL_Keycode kc = SDL_GetKeyFromName(ev.text.text);
544 case SDL_WINDOWEVENT: {
545 if (ev.window.event == SDL_WINDOWEVENT_EXPOSED) {
547 this->
MakeDirty(0, 0, _screen.width, _screen.height);
548 }
else if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
549 int w = std::max(ev.window.data1, 64);
550 int h = std::max(ev.window.data2, 64);
552 }
else if (ev.window.event == SDL_WINDOWEVENT_ENTER) {
554 _cursor.in_window =
true;
556 SDL_SetRelativeMouseMode(SDL_FALSE);
557 }
else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) {
560 _cursor.in_window =
false;
576 if (SDL_WasInit(SDL_INIT_VIDEO) != 0)
return std::nullopt;
578#ifdef SDL_HINT_APP_NAME
579 SDL_SetHint(SDL_HINT_APP_NAME,
"OpenTTD");
582 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
return SDL_GetError();
595 if (error)
return error;
608 if (error)
return error;
610#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
615 if (!SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE,
"0"))
return SDL_GetError();
622 return SDL_GetError();
625 const char *dname = SDL_GetCurrentVideoDriver();
626 Debug(driver, 1,
"SDL2: using driver '{}'", dname);
639 this->is_game_threaded =
false;
649 SDL_QuitSubSystem(SDL_INIT_VIDEO);
650 if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
657 uint32_t mod = SDL_GetModState();
658 const Uint8 *keys = SDL_GetKeyboardState(
nullptr);
689 extern void PostMainLoop();
692 emscripten_cancel_main_loop();
693 emscripten_exit_pointerlock();
698 EM_ASM(
if (window[
"openttd_bootstrap_reload"]) openttd_bootstrap_reload());
700 EM_ASM(
if (window[
"openttd_exit"]) openttd_exit());
710#ifndef __EMSCRIPTEN__
719 emscripten_set_main_loop_arg(&this->EmscriptenLoop,
this, 0, 1);
723 while (!_exit_game) {
745 if (SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(this->
sdl_window), &dm) < 0) {
746 Debug(driver, 0,
"SDL_GetCurrentDisplayMode() failed: {}", SDL_GetError());
748 SDL_SetWindowSize(this->
sdl_window, dm.w, dm.h);
752 Debug(driver, 1,
"SDL2: Setting {}", fullscreen ?
"fullscreen" :
"windowed");
753 int ret = SDL_SetWindowFullscreen(this->
sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
756 _fullscreen = fullscreen;
757 if (!fullscreen) SDL_SetWindowSize(this->
sdl_window, w, h);
759 Debug(driver, 0,
"SDL_SetWindowFullscreen() failed: {}", SDL_GetError());
776 SDL_DisplayMode mode;
779 return {
static_cast<uint
>(mode.w),
static_cast<uint
>(mode.h) };
788 assert(_screen.dst_ptr !=
nullptr);
795 if (_screen.dst_ptr !=
nullptr) {
798 _screen.dst_ptr =
nullptr;
807 SDL_DisableScreenSaver();
809 SDL_EnableScreenSaver();
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
virtual void PostResize()
Post resize event.
virtual std::string_view GetName() const =0
Get the name of this driver.
std::optional< std::string_view > Start(const StringList ¶m) override
Start this driver.
bool buffer_locked
Video buffer was locked by the main thread.
Dimension GetScreenSize() const override
Get the resolution of the main screen.
bool PollEvent() override
Process a single system event.
void EditBoxLostFocus() override
This is called to indicate that an edit box has lost focus, text input mode should be disabled.
virtual void ReleaseVideoPointer()=0
Hand video buffer back to the painting backend.
void ClaimMousePointer() override
Claim the exclusive rights for the mouse pointer.
virtual void * GetVideoPointer()=0
Get a pointer to the video buffer.
void LoopOnce()
Run a single tick of the game/main loop.
bool AfterBlitterChange() override
Callback invoked after the blitter was changed.
virtual bool AllocateBackingStore(int w, int h, bool force=false)=0
(Re-)create the backing store.
void InputLoop() override
Handle input logic, is CTRL pressed, should we fast-forward, etc.
Palette local_palette
Current palette to use for drawing.
void MainLoop() override
Perform the actual drawing.
std::optional< std::string_view > Initialize()
Initialize the driver.
std::string driver_info
Information string about selected driver.
void UnlockVideoBuffer() override
Unlock a previously locked video buffer.
void ClientSizeChanged(int w, int h, bool force)
Indicate to the driver the client-size might have changed.
int startup_display
The display to show OpenTTD on when starting.
void MakeDirty(int left, int top, int width, int height) override
Mark a particular area dirty.
virtual bool CreateMainWindow(uint w, uint h, uint flags=0)
Create the main window.
std::vector< int > GetListOfMonitorRefreshRates() override
Get a list of refresh rates of each available monitor.
void SetScreensaverInhibited(bool inhibited) override
Prevents the system from going to sleep.
bool edit_box_focused
This is true to indicate that keyboard input is in text input mode, and SDL_TEXTINPUT events are enab...
bool ChangeResolution(int w, int h) override
Change the resolution of the window.
void Stop() override
Stop this driver.
bool CreateMainSurface(uint w, uint h, bool resize)
Create/update the surfaces to draw OpenTTD on.
bool LockVideoBuffer() override
Make sure the video buffer is ready for drawing.
void EditBoxGainedFocus() override
This is called to indicate that an edit box has gained focus, text input mode should be enabled.
Rect dirty_rect
Rectangle encompassing the dirty area of the video buffer.
void CheckPaletteAnim() override
Process any pending palette animation.
struct SDL_Window * sdl_window
Main SDL window.
bool ToggleFullscreen(bool fullscreen) override
Change the full screen setting.
virtual Dimension GetScreenSize() const
Get the resolution of the main screen.
bool fast_forward_key_pressed
The fast-forward key is being pressed.
void Tick()
Give the video-driver a tick.
void SleepTillNextTick()
Sleep till the next tick is about to happen.
void StartGameThread()
Start the loop for game-tick.
static std::string GetCaption()
Get the caption to use for the game's title bar.
void StopGameThread()
Stop the loop for the game-tick.
void UpdateAutoResolution()
Apply resolution auto-detection and clamp to sensible defaults.
static const Dimension _default_resolutions[]
List of common display/window sizes.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
int GetDriverParamInt(const StringList &parm, std::string_view name, int def)
Get an integer parameter the list of parameters.
bool GetDriverParamBool(const StringList &parm, std::string_view name)
Get a boolean parameter the list of parameters.
std::vector< Dimension > _resolutions
List of resolutions.
Dimension _cur_resolution
The current resolution.
bool _rightclick_emulate
Whether right clicking is emulated.
Factory to 'query' all available blitters.
std::string FioFindFullPath(Subdirectory subdir, std::string_view filename)
Find a path to the filename in one of the search directories.
Functions for standard in/out file operations.
@ Baseset
Subdirectory for all base data (base sets, intro game).
Types for recording game performance data.
Rect BoundingRect(const Rect &r1, const Rect &r2)
Compute the bounding rectangle around two rectangles.
bool _shift_pressed
Is Shift pressed?
bool _left_button_down
Is left mouse button pressed?
bool _ctrl_pressed
Is Ctrl pressed?
bool _left_button_clicked
Is left mouse button clicked?
bool _right_button_clicked
Is right mouse button clicked?
DirectionKeys _dirkeys
Pressed direction keys.
bool _right_button_down
Is right mouse button pressed?
Functions related to the gfx engine.
void HandleCtrlChanged()
State of CONTROL key has changed.
void GameSizeChanged()
Size of the application screen changed.
void HandleMouseEvents()
Handle a mouse event from the video driver.
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
void HandleTextInput(std::string_view str, bool marked=false, std::optional< size_t > caret=std::nullopt, std::optional< size_t > insert_location=std::nullopt, std::optional< size_t > replacement_end=std::nullopt)
Handle text input.
@ WKC_BACKSLASH
\ Backslash
@ WKC_SLASH
/ Forward slash
@ WKC_SINGLEQUOTE
' Single quote
@ WKC_R_BRACKET
] Right square bracket
@ WKC_L_BRACKET
[ Left square bracket
@ WKC_SEMICOLON
; Semicolon
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr T Delta(const T a, const T b)
Returns the (absolute) difference between two (scalar) variables.
@ Bootstrap
In the content bootstrap process.
bool CopyPalette(Palette &local_palette, bool force_copy)
Copy the current palette if the palette was updated.
Functions related to modal progress.
A number of safeguards to prevent using unsafe methods.
static void GetAvailableVideoMode(uint *w, uint *h)
Get the best resolution given the requested width and height.
static void FindResolutions()
Find and store all possible resolutions into _resolutions.
static std::optional< std::string_view > InitializeSDL()
Initialize the SDL backend if needed.
static uint ConvertSdlKeycodeIntoMy(SDL_Keycode kc)
Convert a SDL_Keycode into our own key codes.
static uint ConvertSdlKeyIntoMy(SDL_Keysym *sym, char32_t *character)
Convert the SDL key into our WindowKeyCode and the printable character (if any).
static uint FindStartupDisplay(uint startup_display)
Find the display we want to start OpenTTD on.
Base of the SDL2 video driver.
ClientSettings _settings_client
The current settings for this game.
Definition of base types and functions in a cross-platform compatible way.
bool IsValidChar(char32_t key, CharSetFilter afilter)
Only allow certain keys.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
std::vector< std::string > StringList
Type for a list of strings.
Dimensions (a width and height) of a rectangle in 2D.
Specification of a rectangle with absolute coordinates of all edges.
Mapping from keycodes in the SDL world to OpenTTD's world.
const uint8_t count
The number of keycodes that are to be mapped.
const WindowKeyCodes map_to
The first of the OpenTTD keycodes of this mapping.
const bool unprintable
Whether this keycode is unprintable.
constexpr SDLVkMapping(SDL_Keycode vk_from, WindowKeyCodes map_to, bool unprintable)
Create a mapping for a single keycode.
const SDL_Keycode vk_from
The first of the SDL keycodes of this mapping.
constexpr SDLVkMapping(SDL_Keycode vk_first, SDL_Keycode vk_last, WindowKeyCodes map_first, WindowKeyCodes map_last, bool unprintable)
Create a mapping for several consecutive keycodes.
std::pair< size_t, char32_t > DecodeUtf8(std::string_view buf)
Decode a character from UTF-8.
Handling of UTF-8 encoded data.
bool FocusedWindowIsConsole()
Check if a console is focused.
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Window functions not directly related to making/drawing windows.