|
@@ -29,151 +29,32 @@
|
|
|
# include <emscripten.h>
|
|
|
# include <emscripten/html5.h>
|
|
|
#endif
|
|
|
|
|
|
#include "../safeguards.h"
|
|
|
|
|
|
static FVideoDriver_SDL iFVideoDriver_SDL;
|
|
|
|
|
|
static SDL_Surface *_sdl_surface;
|
|
|
static SDL_Surface *_sdl_rgb_surface;
|
|
|
static SDL_Surface *_sdl_real_surface;
|
|
|
static SDL_Palette *_sdl_palette;
|
|
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
/** Whether we just had a window-enter event. */
|
|
|
static bool _cursor_new_in_window = false;
|
|
|
#endif
|
|
|
|
|
|
void VideoDriver_SDL::MakeDirty(int left, int top, int width, int height)
|
|
|
void VideoDriver_SDL_Base::MakeDirty(int left, int top, int width, int height)
|
|
|
{
|
|
|
Rect r = {left, top, left + width, top + height};
|
|
|
this->dirty_rect = BoundingRect(this->dirty_rect, r);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::UpdatePalette()
|
|
|
{
|
|
|
SDL_Color pal[256];
|
|
|
|
|
|
for (int i = 0; i != this->local_palette.count_dirty; i++) {
|
|
|
pal[i].r = this->local_palette.palette[this->local_palette.first_dirty + i].r;
|
|
|
pal[i].g = this->local_palette.palette[this->local_palette.first_dirty + i].g;
|
|
|
pal[i].b = this->local_palette.palette[this->local_palette.first_dirty + i].b;
|
|
|
pal[i].a = 0;
|
|
|
}
|
|
|
|
|
|
SDL_SetPaletteColors(_sdl_palette, pal, this->local_palette.first_dirty, this->local_palette.count_dirty);
|
|
|
SDL_SetSurfacePalette(_sdl_surface, _sdl_palette);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::MakePalette()
|
|
|
{
|
|
|
if (_sdl_palette == nullptr) {
|
|
|
_sdl_palette = SDL_AllocPalette(256);
|
|
|
if (_sdl_palette == nullptr) usererror("SDL2: Couldn't allocate palette: %s", SDL_GetError());
|
|
|
}
|
|
|
|
|
|
_cur_palette.first_dirty = 0;
|
|
|
_cur_palette.count_dirty = 256;
|
|
|
this->local_palette = _cur_palette;
|
|
|
this->UpdatePalette();
|
|
|
|
|
|
if (_sdl_surface != _sdl_real_surface) {
|
|
|
/* When using a shadow surface, also set our palette on the real screen. This lets SDL
|
|
|
* allocate as many colors (or approximations) as
|
|
|
* possible, instead of using only the default SDL
|
|
|
* palette. This allows us to get more colors exactly
|
|
|
* right and might allow using better approximations for
|
|
|
* other colors.
|
|
|
*
|
|
|
* Note that colors allocations are tried in-order, so
|
|
|
* this favors colors further up into the palette. Also
|
|
|
* note that if two colors from the same animation
|
|
|
* sequence are approximated using the same color, that
|
|
|
* animation will stop working.
|
|
|
*
|
|
|
* Since changing the system palette causes the colours
|
|
|
* to change right away, and allocations might
|
|
|
* drastically change, we can't use this for animation,
|
|
|
* since that could cause weird coloring between the
|
|
|
* palette change and the blitting below, so we only set
|
|
|
* the real palette during initialisation.
|
|
|
*/
|
|
|
SDL_SetSurfacePalette(_sdl_real_surface, _sdl_palette);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::CheckPaletteAnim()
|
|
|
void VideoDriver_SDL_Base::CheckPaletteAnim()
|
|
|
{
|
|
|
if (_cur_palette.count_dirty == 0) return;
|
|
|
|
|
|
this->local_palette = _cur_palette;
|
|
|
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::Paint()
|
|
|
{
|
|
|
PerformanceMeasurer framerate(PFE_VIDEO);
|
|
|
|
|
|
if (IsEmptyRect(this->dirty_rect) && _cur_palette.count_dirty == 0) return;
|
|
|
|
|
|
if (_cur_palette.count_dirty != 0) {
|
|
|
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
|
|
|
|
|
switch (blitter->UsePaletteAnimation()) {
|
|
|
case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
|
|
|
this->UpdatePalette();
|
|
|
break;
|
|
|
|
|
|
case Blitter::PALETTE_ANIMATION_BLITTER: {
|
|
|
bool need_buf = _screen.dst_ptr == nullptr;
|
|
|
if (need_buf) _screen.dst_ptr = this->GetVideoPointer();
|
|
|
blitter->PaletteAnimate(this->local_palette);
|
|
|
if (need_buf) {
|
|
|
this->ReleaseVideoPointer();
|
|
|
_screen.dst_ptr = nullptr;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case Blitter::PALETTE_ANIMATION_NONE:
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
NOT_REACHED();
|
|
|
}
|
|
|
_cur_palette.count_dirty = 0;
|
|
|
}
|
|
|
|
|
|
SDL_Rect r = { this->dirty_rect.left, this->dirty_rect.top, this->dirty_rect.right - this->dirty_rect.left, this->dirty_rect.bottom - this->dirty_rect.top };
|
|
|
|
|
|
if (_sdl_surface != _sdl_real_surface) {
|
|
|
SDL_BlitSurface(_sdl_surface, &r, _sdl_real_surface, &r);
|
|
|
}
|
|
|
SDL_UpdateWindowSurfaceRects(this->sdl_window, &r, 1);
|
|
|
|
|
|
this->dirty_rect = {};
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::PaintThread()
|
|
|
{
|
|
|
/* First tell the main thread we're started */
|
|
|
std::unique_lock<std::recursive_mutex> lock(*this->draw_mutex);
|
|
|
this->draw_signal->notify_one();
|
|
|
|
|
|
/* Now wait for the first thing to draw! */
|
|
|
this->draw_signal->wait(*this->draw_mutex);
|
|
|
|
|
|
while (this->draw_continue) {
|
|
|
/* Then just draw and wait till we stop */
|
|
|
this->Paint();
|
|
|
this->draw_signal->wait(lock);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* static */ void VideoDriver_SDL::PaintThreadThunk(VideoDriver_SDL *drv)
|
|
|
/* static */ void VideoDriver_SDL_Base::PaintThreadThunk(VideoDriver_SDL_Base *drv)
|
|
|
{
|
|
|
drv->PaintThread();
|
|
|
}
|
|
|
|
|
|
static const Dimension default_resolutions[] = {
|
|
|
{ 640, 480 },
|
|
@@ -250,13 +131,13 @@ static uint FindStartupDisplay(uint star
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::ClientSizeChanged(int w, int h, bool force)
|
|
|
void VideoDriver_SDL_Base::ClientSizeChanged(int w, int h, bool force)
|
|
|
{
|
|
|
/* Allocate backing store of the new size. */
|
|
|
if (this->AllocateBackingStore(w, h, force)) {
|
|
|
/* Mark all palette colours dirty. */
|
|
|
_cur_palette.first_dirty = 0;
|
|
|
_cur_palette.count_dirty = 256;
|
|
@@ -265,13 +146,13 @@ void VideoDriver_SDL::ClientSizeChanged(
|
|
|
BlitterFactory::GetCurrentBlitter()->PostResize();
|
|
|
|
|
|
GameSizeChanged();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::CreateMainWindow(uint w, uint h, uint flags)
|
|
|
bool VideoDriver_SDL_Base::CreateMainWindow(uint w, uint h, uint flags)
|
|
|
{
|
|
|
if (this->sdl_window != nullptr) return true;
|
|
|
|
|
|
flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
|
|
|
|
|
|
if (_fullscreen) {
|
|
@@ -312,13 +193,13 @@ bool VideoDriver_SDL::CreateMainWindow(u
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::CreateMainSurface(uint w, uint h, bool resize)
|
|
|
bool VideoDriver_SDL_Base::CreateMainSurface(uint w, uint h, bool resize)
|
|
|
{
|
|
|
GetAvailableVideoMode(&w, &h);
|
|
|
DEBUG(driver, 1, "SDL2: using mode %ux%u", w, h);
|
|
|
|
|
|
if (!this->CreateMainWindow(w, h)) return false;
|
|
|
if (resize) SDL_SetWindowSize(this->sdl_window, w, h);
|
|
@@ -329,82 +210,36 @@ bool VideoDriver_SDL::CreateMainSurface(
|
|
|
* appropriate event to know this. */
|
|
|
if (_fullscreen) _cursor.in_window = true;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::AllocateBackingStore(int w, int h, bool force)
|
|
|
{
|
|
|
int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
|
|
|
|
|
|
_sdl_real_surface = SDL_GetWindowSurface(this->sdl_window);
|
|
|
if (_sdl_real_surface == nullptr) usererror("SDL2: Couldn't get window surface: %s", SDL_GetError());
|
|
|
|
|
|
if (!force && w == _sdl_real_surface->w && h == _sdl_real_surface->h) return false;
|
|
|
|
|
|
/* Free any previously allocated rgb surface. */
|
|
|
if (_sdl_rgb_surface != nullptr) {
|
|
|
SDL_FreeSurface(_sdl_rgb_surface);
|
|
|
_sdl_rgb_surface = nullptr;
|
|
|
}
|
|
|
|
|
|
if (bpp == 8) {
|
|
|
_sdl_rgb_surface = SDL_CreateRGBSurface(0, w, h, 8, 0, 0, 0, 0);
|
|
|
if (_sdl_rgb_surface == nullptr) usererror("SDL2: Couldn't allocate shadow surface: %s", SDL_GetError());
|
|
|
|
|
|
_sdl_surface = _sdl_rgb_surface;
|
|
|
} else {
|
|
|
_sdl_surface = _sdl_real_surface;
|
|
|
}
|
|
|
|
|
|
/* X11 doesn't appreciate it if we invalidate areas outside the window
|
|
|
* if shared memory is enabled (read: it crashes). So, as we might have
|
|
|
* gotten smaller, reset our dirty rects. GameSizeChanged() a bit lower
|
|
|
* will mark the whole screen dirty again anyway, but this time with the
|
|
|
* new dimensions. */
|
|
|
this->dirty_rect = {};
|
|
|
|
|
|
_screen.width = _sdl_surface->w;
|
|
|
_screen.height = _sdl_surface->h;
|
|
|
_screen.pitch = _sdl_surface->pitch / (bpp / 8);
|
|
|
_screen.dst_ptr = this->GetVideoPointer();
|
|
|
|
|
|
this->MakePalette();
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void *VideoDriver_SDL::GetVideoPointer()
|
|
|
{
|
|
|
return _sdl_surface->pixels;
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::ClaimMousePointer()
|
|
|
bool VideoDriver_SDL_Base::ClaimMousePointer()
|
|
|
{
|
|
|
SDL_ShowCursor(0);
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
|
#endif
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* This is called to indicate that an edit box has gained focus, text input mode should be enabled.
|
|
|
*/
|
|
|
void VideoDriver_SDL::EditBoxGainedFocus()
|
|
|
void VideoDriver_SDL_Base::EditBoxGainedFocus()
|
|
|
{
|
|
|
if (!this->edit_box_focused) {
|
|
|
SDL_StartTextInput();
|
|
|
this->edit_box_focused = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* This is called to indicate that an edit box has lost focus, text input mode should be disabled.
|
|
|
*/
|
|
|
void VideoDriver_SDL::EditBoxLostFocus()
|
|
|
void VideoDriver_SDL_Base::EditBoxLostFocus()
|
|
|
{
|
|
|
if (this->edit_box_focused) {
|
|
|
SDL_StopTextInput();
|
|
|
this->edit_box_focused = false;
|
|
|
}
|
|
|
}
|
|
@@ -532,13 +367,13 @@ static uint ConvertSdlKeycodeIntoMy(SDL_
|
|
|
SDL_Scancode sc = SDL_GetScancodeFromKey(kc);
|
|
|
if (sc == SDL_SCANCODE_GRAVE) key = WKC_BACKQUOTE;
|
|
|
|
|
|
return key;
|
|
|
}
|
|
|
|
|
|
int VideoDriver_SDL::PollEvent()
|
|
|
int VideoDriver_SDL_Base::PollEvent()
|
|
|
{
|
|
|
SDL_Event ev;
|
|
|
|
|
|
if (!SDL_PollEvent(&ev)) return -2;
|
|
|
|
|
|
switch (ev.type) {
|
|
@@ -696,26 +531,26 @@ static const char *InitializeSDL()
|
|
|
if (SDL_WasInit(SDL_INIT_VIDEO) != 0) return nullptr;
|
|
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) return SDL_GetError();
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
const char *VideoDriver_SDL::Initialize()
|
|
|
const char *VideoDriver_SDL_Base::Initialize()
|
|
|
{
|
|
|
this->UpdateAutoResolution();
|
|
|
|
|
|
const char *error = InitializeSDL();
|
|
|
if (error != nullptr) return error;
|
|
|
|
|
|
FindResolutions();
|
|
|
DEBUG(driver, 2, "Resolution for display: %ux%u", _cur_resolution.width, _cur_resolution.height);
|
|
|
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
const char *VideoDriver_SDL::Start(const StringList &parm)
|
|
|
const char *VideoDriver_SDL_Base::Start(const StringList &parm)
|
|
|
{
|
|
|
if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return "Only real blitters supported";
|
|
|
|
|
|
const char *error = this->Initialize();
|
|
|
if (error != nullptr) return error;
|
|
|
|
|
@@ -746,21 +581,21 @@ const char *VideoDriver_SDL::Start(const
|
|
|
SDL_StopTextInput();
|
|
|
this->edit_box_focused = false;
|
|
|
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::Stop()
|
|
|
void VideoDriver_SDL_Base::Stop()
|
|
|
{
|
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
|
if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) {
|
|
|
SDL_Quit(); // If there's nothing left, quit SDL
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::InputLoop()
|
|
|
void VideoDriver_SDL_Base::InputLoop()
|
|
|
{
|
|
|
uint32 mod = SDL_GetModState();
|
|
|
const Uint8 *keys = SDL_GetKeyboardState(NULL);
|
|
|
|
|
|
bool old_ctrl_pressed = _ctrl_pressed;
|
|
|
|
|
@@ -787,13 +622,13 @@ void VideoDriver_SDL::InputLoop()
|
|
|
(keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
|
|
|
(keys[SDL_SCANCODE_DOWN] ? 8 : 0);
|
|
|
|
|
|
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::LoopOnce()
|
|
|
void VideoDriver_SDL_Base::LoopOnce()
|
|
|
{
|
|
|
InteractiveRandom(); // randomness
|
|
|
|
|
|
while (PollEvent() == -1) {}
|
|
|
if (_exit_game) {
|
|
|
#ifdef __EMSCRIPTEN__
|
|
@@ -821,26 +656,26 @@ void VideoDriver_SDL::LoopOnce()
|
|
|
* downtime between each iteration, so no need to sleep. */
|
|
|
#ifndef __EMSCRIPTEN__
|
|
|
this->SleepTillNextTick();
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::MainLoop()
|
|
|
void VideoDriver_SDL_Base::MainLoop()
|
|
|
{
|
|
|
if (this->draw_threaded) {
|
|
|
/* Initialise the mutex first, because that's the thing we *need*
|
|
|
* directly in the newly created thread. */
|
|
|
this->draw_mutex = new std::recursive_mutex();
|
|
|
if (this->draw_mutex == nullptr) {
|
|
|
this->draw_threaded = false;
|
|
|
} else {
|
|
|
draw_lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
|
|
|
this->draw_signal = new std::condition_variable_any();
|
|
|
this->draw_continue = true;
|
|
|
|
|
|
this->draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &VideoDriver_SDL::PaintThreadThunk, this);
|
|
|
this->draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &VideoDriver_SDL_Base::PaintThreadThunk, this);
|
|
|
|
|
|
/* Free the mutex if we won't be able to use it. */
|
|
|
if (!this->draw_threaded) {
|
|
|
draw_lock.unlock();
|
|
|
draw_lock.release();
|
|
|
delete this->draw_mutex;
|
|
@@ -865,13 +700,13 @@ void VideoDriver_SDL::MainLoop()
|
|
|
}
|
|
|
|
|
|
MainLoopCleanup();
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::MainLoopCleanup()
|
|
|
void VideoDriver_SDL_Base::MainLoopCleanup()
|
|
|
{
|
|
|
if (this->draw_mutex != nullptr) {
|
|
|
this->draw_continue = false;
|
|
|
/* Sending signal if there is no thread blocked
|
|
|
* is very valid and results in noop */
|
|
|
this->draw_signal->notify_one();
|
|
@@ -893,21 +728,21 @@ void VideoDriver_SDL::MainLoopCleanup()
|
|
|
* openttd_main() is never executed. */
|
|
|
EM_ASM(if (window["openttd_syncfs"]) openttd_syncfs());
|
|
|
EM_ASM(if (window["openttd_exit"]) openttd_exit());
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::ChangeResolution(int w, int h)
|
|
|
bool VideoDriver_SDL_Base::ChangeResolution(int w, int h)
|
|
|
{
|
|
|
std::unique_lock<std::recursive_mutex> lock;
|
|
|
if (this->draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
|
|
|
|
|
|
return CreateMainSurface(w, h, true);
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen)
|
|
|
bool VideoDriver_SDL_Base::ToggleFullscreen(bool fullscreen)
|
|
|
{
|
|
|
std::unique_lock<std::recursive_mutex> lock;
|
|
|
if (this->draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*this->draw_mutex);
|
|
|
|
|
|
int w, h;
|
|
|
|
|
@@ -934,52 +769,52 @@ bool VideoDriver_SDL::ToggleFullscreen(b
|
|
|
DEBUG(driver, 0, "SDL_SetWindowFullscreen() failed: %s", SDL_GetError());
|
|
|
}
|
|
|
|
|
|
return ret == 0;
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::AfterBlitterChange()
|
|
|
bool VideoDriver_SDL_Base::AfterBlitterChange()
|
|
|
{
|
|
|
assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0);
|
|
|
int w, h;
|
|
|
SDL_GetWindowSize(this->sdl_window, &w, &h);
|
|
|
return CreateMainSurface(w, h, false);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::AcquireBlitterLock()
|
|
|
void VideoDriver_SDL_Base::AcquireBlitterLock()
|
|
|
{
|
|
|
if (this->draw_mutex != nullptr) this->draw_mutex->lock();
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::ReleaseBlitterLock()
|
|
|
void VideoDriver_SDL_Base::ReleaseBlitterLock()
|
|
|
{
|
|
|
if (this->draw_mutex != nullptr) this->draw_mutex->unlock();
|
|
|
}
|
|
|
|
|
|
Dimension VideoDriver_SDL::GetScreenSize() const
|
|
|
Dimension VideoDriver_SDL_Base::GetScreenSize() const
|
|
|
{
|
|
|
SDL_DisplayMode mode;
|
|
|
if (SDL_GetCurrentDisplayMode(this->startup_display, &mode) != 0) return VideoDriver::GetScreenSize();
|
|
|
|
|
|
return { static_cast<uint>(mode.w), static_cast<uint>(mode.h) };
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_SDL::LockVideoBuffer()
|
|
|
bool VideoDriver_SDL_Base::LockVideoBuffer()
|
|
|
{
|
|
|
if (this->buffer_locked) return false;
|
|
|
this->buffer_locked = true;
|
|
|
|
|
|
if (this->draw_threaded) this->draw_lock.lock();
|
|
|
|
|
|
_screen.dst_ptr = this->GetVideoPointer();
|
|
|
assert(_screen.dst_ptr != nullptr);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void VideoDriver_SDL::UnlockVideoBuffer()
|
|
|
void VideoDriver_SDL_Base::UnlockVideoBuffer()
|
|
|
{
|
|
|
if (_screen.dst_ptr != nullptr) {
|
|
|
/* Hand video buffer back to the drawing backend. */
|
|
|
this->ReleaseVideoPointer();
|
|
|
_screen.dst_ptr = nullptr;
|
|
|
}
|