Changeset - r24918:e55bb9103b0b
[Not reviewed]
master
0 5 2
Michael Lutz - 3 years ago 2021-02-11 20:03:18
michi@icosahedron.de
Codechange: [SDL2] Split driver in base-part and default backend
7 files changed with 280 insertions and 214 deletions:
0 comments (0 inline, 0 general)
src/video/CMakeLists.txt
Show inline comments
 
@@ -13,24 +13,26 @@ if(NOT OPTION_DEDICATED)
 
        CONDITION OPENGL_FOUND
 
    )
 

	
 
    add_files(
 
        sdl_v.cpp
 
        sdl_v.h
 
        CONDITION SDL_FOUND
 
    )
 

	
 
    add_files(
 
        sdl2_v.cpp
 
        sdl2_v.h
 
        sdl2_default_v.cpp
 
        sdl2_default_v.h
 
        CONDITION SDL2_FOUND
 
    )
 

	
 
    add_files(
 
        sdl2_opengl_v.cpp
 
        sdl2_opengl_v.h
 
        CONDITION SDL2_FOUND AND OPENGL_FOUND
 
    )
 

	
 
    add_files(
 
        win32_v.cpp
 
        win32_v.h
src/video/sdl2_default_v.cpp
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file sdl2_default_v.cpp Implementation of the default backend for SDL2 video driver. */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../gfx_func.h"
 
#include "../rev.h"
 
#include "../blitter/factory.hpp"
 
#include "../network/network.h"
 
#include "../thread.h"
 
#include "../progress.h"
 
#include "../core/random_func.hpp"
 
#include "../core/math_func.hpp"
 
#include "../core/mem_func.hpp"
 
#include "../core/geometry_func.hpp"
 
#include "../fileio_func.h"
 
#include "../framerate_type.h"
 
#include "../window_func.h"
 
#include "sdl2_default_v.h"
 
#include <SDL.h>
 
#include <mutex>
 
#include <condition_variable>
 
#ifdef __EMSCRIPTEN__
 
#	include <emscripten.h>
 
#	include <emscripten/html5.h>
 
#endif
 

	
 
#include "../safeguards.h"
 

	
 
static FVideoDriver_SDL_Default iFVideoDriver_SDL_Default;
 

	
 
static SDL_Surface *_sdl_surface;
 
static SDL_Surface *_sdl_rgb_surface;
 
static SDL_Surface *_sdl_real_surface;
 
static SDL_Palette *_sdl_palette;
 

	
 
void VideoDriver_SDL_Default::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_Default::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_Default::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_Default::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);
 
	}
 
}
 

	
 
bool VideoDriver_SDL_Default::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_Default::GetVideoPointer()
 
{
 
	return _sdl_surface->pixels;
 
}
src/video/sdl2_default_v.h
Show inline comments
 
new file 100644
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file sdl2_default_v.h Default backend of the SDL2 video driver. */
 

	
 
#ifndef VIDEO_SDL2_DEFAULT_H
 
#define VIDEO_SDL2_DEFAULT_H
 

	
 
#include "sdl2_v.h"
 

	
 
/** The SDL video driver using default SDL backend. */
 
class VideoDriver_SDL_Default : public VideoDriver_SDL_Base {
 
public:
 
	const char *GetName() const override { return "sdl"; }
 

	
 
protected:
 
	bool AllocateBackingStore(int w, int h, bool force = false) override;
 
	void *GetVideoPointer() override;
 
	void Paint() override;
 
	void PaintThread() override;
 

	
 
	void ReleaseVideoPointer() override {}
 

	
 
private:
 
	void UpdatePalette();
 
	void MakePalette();
 
};
 

	
 
/** Factory for the SDL video driver. */
 
class FVideoDriver_SDL_Default : public DriverFactoryBase {
 
public:
 
	FVideoDriver_SDL_Default() : DriverFactoryBase(Driver::DT_VIDEO, 5, "sdl", "SDL Video Driver") {}
 
	Driver *CreateInstance() const override { return new VideoDriver_SDL_Default(); }
 
};
 

	
 
#endif /* VIDEO_SDL2_DEFAULT_H */
src/video/sdl2_opengl_v.cpp
Show inline comments
 
@@ -41,54 +41,54 @@
 
#include "../safeguards.h"
 

	
 
static FVideoDriver_SDL_OpenGL iFVideoDriver_SDL_OpenGL;
 

	
 
/** Platform-specific callback to get an OpenGL funtion pointer. */
 
static OGLProc GetOGLProcAddressCallback(const char *proc)
 
{
 
	return reinterpret_cast<OGLProc>(SDL_GL_GetProcAddress(proc));
 
}
 

	
 
bool VideoDriver_SDL_OpenGL::CreateMainWindow(uint w, uint h, uint flags)
 
{
 
	return this->VideoDriver_SDL::CreateMainWindow(w, h, SDL_WINDOW_OPENGL);
 
	return this->VideoDriver_SDL_Base::CreateMainWindow(w, h, SDL_WINDOW_OPENGL);
 
}
 

	
 
const char *VideoDriver_SDL_OpenGL::Start(const StringList &param)
 
{
 
	const char *error = VideoDriver_SDL::Start(param);
 
	const char *error = VideoDriver_SDL_Base::Start(param);
 
	if (error != nullptr) return error;
 

	
 
	error = this->AllocateContext();
 
	if (error != nullptr) {
 
		this->Stop();
 
		return error;
 
	}
 

	
 
	/* Now we have a OpenGL context, force a client-size-changed event,
 
	 * so all buffers are allocated correctly. */
 
	int w, h;
 
	SDL_GetWindowSize(this->sdl_window, &w, &h);
 
	this->ClientSizeChanged(w, h, true);
 

	
 
	SDL_GL_SetSwapInterval(GetDriverParamBool(param, "vsync") ? 1 : 0);
 
	this->draw_threaded = false;
 

	
 
	return nullptr;
 
}
 

	
 
void VideoDriver_SDL_OpenGL::Stop()
 
{
 
	this->DestroyContext();
 
	this->VideoDriver_SDL::Stop();
 
	this->VideoDriver_SDL_Base::Stop();
 
}
 

	
 
void VideoDriver_SDL_OpenGL::DestroyContext()
 
{
 
	OpenGLBackend::Destroy();
 

	
 
	if (this->gl_context != nullptr) {
 
		SDL_GL_DeleteContext(this->gl_context);
 
		this->gl_context = nullptr;
 
	}
 
}
 

	
src/video/sdl2_opengl_v.h
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file sdl2_opengl_v.h OpenGL backend of the SDL2 video driver. */
 

	
 
#include "sdl2_v.h"
 

	
 
/** The OpenGL video driver for windows. */
 
class VideoDriver_SDL_OpenGL : public VideoDriver_SDL {
 
class VideoDriver_SDL_OpenGL : public VideoDriver_SDL_Base {
 
public:
 
	VideoDriver_SDL_OpenGL() : gl_context(nullptr), anim_buffer(nullptr) {}
 

	
 
	const char *Start(const StringList &param) override;
 

	
 
	void Stop() override;
 

	
 
	bool HasEfficient8Bpp() const override { return true; }
 

	
 
	bool UseSystemCursor() override { return true; }
 

	
 
	void ClearSystemSprites() override;
 
@@ -27,24 +27,26 @@ public:
 
	bool HasAnimBuffer() override { return true; }
 
	uint8 *GetAnimBuffer() override { return this->anim_buffer; }
 

	
 
	const char *GetName() const override { return "sdl-opengl"; }
 

	
 
protected:
 
	bool AllocateBackingStore(int w, int h, bool force = false) override;
 
	void *GetVideoPointer() override;
 
	void ReleaseVideoPointer() override;
 
	void Paint() override;
 
	bool CreateMainWindow(uint w, uint h, uint flags) override;
 

	
 
	void PaintThread() override {}
 

	
 
private:
 
	void  *gl_context;  ///< OpenGL context.
 
	uint8 *anim_buffer; ///< Animation buffer from OpenGL back-end.
 

	
 
	const char *AllocateContext();
 
	void DestroyContext();
 
};
 

	
 
/** The factory for SDL' OpenGL video driver. */
 
class FVideoDriver_SDL_OpenGL : public DriverFactoryBase {
 
public:
 
	FVideoDriver_SDL_OpenGL() : DriverFactoryBase(Driver::DT_VIDEO, 8, "sdl-opengl", "SDL OpenGL Video Driver") {}
src/video/sdl2_v.cpp
Show inline comments
 
@@ -23,163 +23,44 @@
 
#include "../framerate_type.h"
 
#include "../window_func.h"
 
#include "sdl2_v.h"
 
#include <SDL.h>
 
#include <mutex>
 
#ifdef __EMSCRIPTEN__
 
#	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 },
 
	{  800,  600 },
 
	{ 1024,  768 },
 
	{ 1152,  864 },
 
	{ 1280,  800 },
 
	{ 1280,  960 },
 
	{ 1280, 1024 },
 
@@ -244,40 +125,40 @@ static uint FindStartupDisplay(uint star
 
	SDL_GetGlobalMouseState(&mx, &my);
 
	for (int display = 0; display < num_displays; ++display) {
 
		SDL_Rect r;
 
		if (SDL_GetDisplayBounds(display, &r) == 0 && IsInsideBS(mx, r.x, r.w) && IsInsideBS(my, r.y, r.h)) {
 
			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);
 
			return display;
 
		}
 
	}
 

	
 
	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;
 
		this->local_palette = _cur_palette;
 

	
 
		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) {
 
		flags |= SDL_WINDOW_FULLSCREEN;
 
	}
 

	
 
	int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
 
	SDL_Rect r;
 
	if (SDL_GetDisplayBounds(this->startup_display, &r) == 0) {
 
@@ -306,111 +187,65 @@ bool VideoDriver_SDL::CreateMainWindow(u
 
			/* Get the colourkey, which will be magenta */
 
			uint32 rgbmap = SDL_MapRGB(icon->format, 255, 0, 255);
 

	
 
			SDL_SetColorKey(icon, SDL_TRUE, rgbmap);
 
			SDL_SetWindowIcon(this->sdl_window, icon);
 
			SDL_FreeSurface(icon);
 
		}
 
	}
 

	
 
	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);
 
	this->ClientSizeChanged(w, h, true);
 

	
 
	/* When in full screen, we will always have the mouse cursor
 
	 * within the window, even though SDL does not give us the
 
	 * 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;
 
	}
 
}
 

	
 

	
 
struct SDLVkMapping {
 
	SDL_Keycode vk_from;
 
	byte vk_count;
 
	byte map_to;
 
@@ -526,25 +361,25 @@ static uint ConvertSdlKeycodeIntoMy(SDL_
 
			break;
 
		}
 
	}
 

	
 
	/* check scancode for BACKQUOTE key, because we want the key left
 
	 * of "1", not anything else (on non-US keyboards) */
 
	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) {
 
		case SDL_MOUSEMOTION:
 
#ifdef __EMSCRIPTEN__
 
			if (_cursor_new_in_window) {
 
				/* The cursor just moved into the window; this means we don't
 
				 * know the absolutely position yet to move relative from.
 
				 * Before this time, SDL didn't know it either, and this is
 
@@ -690,38 +525,38 @@ static const char *InitializeSDL()
 
	 * UpdateWindowSurface() to update the window's texture instead of
 
	 * its surface. */
 
	SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION, "0");
 
	SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
 

	
 
	/* Check if the video-driver is already initialized. */
 
	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;
 

	
 
	this->startup_display = FindStartupDisplay(GetDriverParamInt(parm, "display", -1));
 

	
 
	if (!CreateMainSurface(_cur_resolution.width, _cur_resolution.height, false)) {
 
		return SDL_GetError();
 
	}
 

	
 
@@ -740,33 +575,33 @@ const char *VideoDriver_SDL::Start(const
 
	 * driver is detected.
 
	 */
 
	if (strcmp(dname, "wayland") == 0) {
 
		this->draw_threaded = false;
 
	}
 

	
 
	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;
 

	
 
	_ctrl_pressed  = !!(mod & KMOD_CTRL);
 
	_shift_pressed = !!(mod & KMOD_SHIFT);
 

	
 
#if defined(_DEBUG)
 
	if (_shift_pressed)
 
#else
 
@@ -781,25 +616,25 @@ void VideoDriver_SDL::InputLoop()
 
	}
 

	
 
	/* Determine which directional keys are down. */
 
	_dirkeys =
 
		(keys[SDL_SCANCODE_LEFT]  ? 1 : 0) |
 
		(keys[SDL_SCANCODE_UP]    ? 2 : 0) |
 
		(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__
 
		/* Emscripten is event-driven, and as such the main loop is inside
 
		 * the browser. So if _exit_game goes true, the main loop ends (the
 
		 * cancel call), but we still have to call the cleanup that is
 
		 * normally done at the end of the main loop for non-Emscripten.
 
		 * After that, Emscripten just halts, and the HTML shows a nice
 
		 * "bye, see you next time" message. */
 
@@ -815,38 +650,38 @@ void VideoDriver_SDL::LoopOnce()
 
		} else {
 
			this->Paint();
 
		}
 
	}
 

	
 
/* Emscripten is running an event-based mainloop; there is already some
 
 * 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;
 
				delete this->draw_signal;
 
				this->draw_mutex = nullptr;
 
				this->draw_signal = nullptr;
 
			} else {
 
				/* Wait till the draw mutex has started itself. */
 
				this->draw_signal->wait(*this->draw_mutex);
 
@@ -859,25 +694,25 @@ void VideoDriver_SDL::MainLoop()
 
#ifdef __EMSCRIPTEN__
 
	/* Run the main loop event-driven, based on RequestAnimationFrame. */
 
	emscripten_set_main_loop_arg(&this->EmscriptenLoop, this, 0, 1);
 
#else
 
	while (!_exit_game) {
 
		LoopOnce();
 
	}
 

	
 
	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();
 
		if (draw_lock.owns_lock()) draw_lock.unlock();
 
		draw_lock.release();
 
		draw_thread.join();
 

	
 
		delete this->draw_mutex;
 
		delete this->draw_signal;
 
@@ -887,33 +722,33 @@ void VideoDriver_SDL::MainLoopCleanup()
 
	}
 

	
 
#ifdef __EMSCRIPTEN__
 
	emscripten_exit_pointerlock();
 
	/* In effect, the game ends here. As emscripten_set_main_loop() caused
 
	 * the stack to be unwound, the code after MainLoop() in
 
	 * 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;
 

	
 
	/* Remember current window size */
 
	if (fullscreen) {
 
		SDL_GetWindowSize(this->sdl_window, &w, &h);
 

	
 
		/* Find fullscreen window size */
 
		SDL_DisplayMode dm;
 
@@ -928,62 +763,62 @@ bool VideoDriver_SDL::ToggleFullscreen(b
 
	int ret = SDL_SetWindowFullscreen(this->sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
 
	if (ret == 0) {
 
		/* Switching resolution succeeded, set fullscreen value of window. */
 
		_fullscreen = fullscreen;
 
		if (!fullscreen) SDL_SetWindowSize(this->sdl_window, w, h);
 
	} else {
 
		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;
 
	}
 

	
 
	if (this->draw_threaded) this->draw_lock.unlock();
 
	this->buffer_locked = false;
 
}
src/video/sdl2_v.h
Show inline comments
 
@@ -6,27 +6,27 @@
 
 */
 

	
 
/** @file sdl2_v.h Base of the SDL2 video driver. */
 

	
 
#ifndef VIDEO_SDL_H
 
#define VIDEO_SDL_H
 

	
 
#include <condition_variable>
 

	
 
#include "video_driver.hpp"
 

	
 
/** The SDL video driver. */
 
class VideoDriver_SDL : public VideoDriver {
 
class VideoDriver_SDL_Base : public VideoDriver {
 
public:
 
	VideoDriver_SDL() : sdl_window(nullptr) {}
 
	VideoDriver_SDL_Base() : sdl_window(nullptr) {}
 

	
 
	const char *Start(const StringList &param) override;
 

	
 
	void Stop() override;
 

	
 
	void MakeDirty(int left, int top, int width, int height) override;
 

	
 
	void MainLoop() override;
 

	
 
	bool ChangeResolution(int w, int h) override;
 

	
 
	bool ToggleFullscreen(bool fullscreen) override;
 
@@ -50,63 +50,51 @@ protected:
 
	Palette local_palette; ///< Copy of _cur_palette.
 
	bool draw_threaded; ///< Whether the drawing is/may be done in a separate thread.
 
	std::recursive_mutex *draw_mutex = nullptr; ///< Mutex to keep the access to the shared memory controlled.
 
	std::condition_variable_any *draw_signal = nullptr; ///< Signal to draw the next frame.
 
	volatile bool draw_continue; ///< Should we keep continue drawing?
 
	bool buffer_locked; ///< Video buffer was locked by the main thread.
 
	Rect dirty_rect; ///< Rectangle encompassing the dirty area of the video buffer.
 

	
 
	Dimension GetScreenSize() const override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void Paint() override;
 
	void PaintThread() override;
 
	void CheckPaletteAnim() override;
 

	
 
	/** Indicate to the driver the client-side might have changed. */
 
	void ClientSizeChanged(int w, int h, bool force);
 

	
 
	/** (Re-)create the backing store. */
 
	virtual bool AllocateBackingStore(int w, int h, bool force = false);
 
	virtual bool AllocateBackingStore(int w, int h, bool force = false) = 0;
 
	/** Get a pointer to the video buffer. */
 
	virtual void *GetVideoPointer();
 
	virtual void *GetVideoPointer() = 0;
 
	/** Hand video buffer back to the painting backend. */
 
	virtual void ReleaseVideoPointer() {}
 
	virtual void ReleaseVideoPointer() = 0;
 
	/** Create the main window. */
 
	virtual bool CreateMainWindow(uint w, uint h, uint flags = 0);
 

	
 
private:
 
	int PollEvent();
 
	void LoopOnce();
 
	void MainLoopCleanup();
 
	bool CreateMainSurface(uint w, uint h, bool resize);
 
	const char *Initialize();
 
	bool CreateMainWindow(uint w, uint h);
 
	void UpdatePalette();
 
	void MakePalette();
 

	
 
#ifdef __EMSCRIPTEN__
 
	/* Convert a constant pointer back to a non-constant pointer to a member function. */
 
	static void EmscriptenLoop(void *self) { ((VideoDriver_SDL *)self)->LoopOnce(); }
 
	static void EmscriptenLoop(void *self) { ((VideoDriver_SDL_Base *)self)->LoopOnce(); }
 
#endif
 

	
 
	/**
 
	 * This is true to indicate that keyboard input is in text input mode, and SDL_TEXTINPUT events are enabled.
 
	 */
 
	bool edit_box_focused;
 

	
 
	int startup_display;
 
	std::thread draw_thread;
 
	std::unique_lock<std::recursive_mutex> draw_lock;
 

	
 
	static void PaintThreadThunk(VideoDriver_SDL *drv);
 
};
 

	
 
/** Factory for the SDL video driver. */
 
class FVideoDriver_SDL : public DriverFactoryBase {
 
public:
 
	FVideoDriver_SDL() : DriverFactoryBase(Driver::DT_VIDEO, 5, "sdl", "SDL Video Driver") {}
 
	Driver *CreateInstance() const override { return new VideoDriver_SDL(); }
 
	static void PaintThreadThunk(VideoDriver_SDL_Base *drv);
 
};
 

	
 
#endif /* VIDEO_SDL_H */
0 comments (0 inline, 0 general)