Changeset - r24878:fb3a0559b42f
[Not reviewed]
master
0 2 0
Michael Lutz - 3 years ago 2021-01-16 15:43:03
michi@icosahedron.de
Codechange: [Win32] Move common initialization and finalization to the video driver base class.
2 files changed with 49 insertions and 39 deletions:
0 comments (0 inline, 0 general)
src/video/win32_v.cpp
Show inline comments
 
@@ -25,49 +25,48 @@
 
#include "../framerate_type.h"
 
#include "win32_v.h"
 
#include <windows.h>
 
#include <imm.h>
 
#include <mutex>
 
#include <condition_variable>
 

	
 
#include "../safeguards.h"
 

	
 
/* Missing define in MinGW headers. */
 
#ifndef MAPVK_VK_TO_CHAR
 
#define MAPVK_VK_TO_CHAR    (2)
 
#endif
 

	
 
#ifndef PM_QS_INPUT
 
#define PM_QS_INPUT 0x20000
 
#endif
 

	
 
static struct {
 
	void *buffer_bits;    ///< Internal rendering buffer.
 
	int width;            ///< Width in pixels of our display surface.
 
	int height;           ///< Height in pixels of our display surface.
 
	int width_org;        ///< Original monitor resolution width, before we changed it.
 
	int height_org;       ///< Original monitor resolution height, before we changed it.
 
	bool fullscreen;      ///< Whether to use (true) fullscreen mode.
 
	bool has_focus;       ///< Does our window have system focus?
 
	bool running;         ///< Is the main loop running?
 
} _wnd;
 

	
 
bool _window_maximize;
 
static Dimension _bck_resolution;
 
DWORD _imm_props;
 

	
 
/** Whether the drawing is/may be done in a separate thread. */
 
static bool _draw_threaded;
 
/** Mutex to keep the access to the shared memory controlled. */
 
static std::recursive_mutex *_draw_mutex = nullptr;
 
/** Signal to draw the next frame. */
 
static std::condition_variable_any *_draw_signal = nullptr;
 
/** Should we keep continue drawing? */
 
static volatile bool _draw_continue;
 
/** Local copy of the palette for use in the drawing thread. */
 
static Palette _local_palette;
 
/** Region of the screen that needs redrawing. */
 
static Rect _dirty_rect;
 

	
 
bool VideoDriver_Win32Base::ClaimMousePointer()
 
{
 
	MyShowCursor(false, true);
 
@@ -121,115 +120,121 @@ static const Win32VkMapping _vk_mapping[
 
	AS(0xDE,  WKC_SINGLEQUOTE),
 
	AS(0xBC,  WKC_COMMA),
 
	AS(0xBD,  WKC_MINUS),
 
	AS(0xBE,  WKC_PERIOD)
 
};
 

	
 
static uint MapWindowsKey(uint sym)
 
{
 
	const Win32VkMapping *map;
 
	uint key = 0;
 

	
 
	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
 
		if ((uint)(sym - map->vk_from) <= map->vk_count) {
 
			key = sym - map->vk_from + map->map_to;
 
			break;
 
		}
 
	}
 

	
 
	if (GetAsyncKeyState(VK_SHIFT)   < 0) key |= WKC_SHIFT;
 
	if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
 
	if (GetAsyncKeyState(VK_MENU)    < 0) key |= WKC_ALT;
 
	return key;
 
}
 

	
 
/** Colour depth to use for fullscreen display modes. */
 
uint8 VideoDriver_Win32Base::GetFullscreenBpp()
 
{
 
	/* Check modes for the relevant fullscreen bpp */
 
	return _support8bpp != S8BPP_HARDWARE ? 32 : BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
 
}
 

	
 
/**
 
 * Instantiate a new window.
 
 * @param full_screen Whether to make a full screen window or not.
 
 * @return True if the window could be created.
 
 */
 
bool VideoDriver_Win32Base::MakeWindow(bool full_screen)
 
{
 
	/* full_screen is whether the new window should be fullscreen,
 
	 * _wnd.fullscreen is whether the current window is. */
 
	_fullscreen = full_screen;
 

	
 
	/* recreate window? */
 
	if ((full_screen || _wnd.fullscreen) && this->main_wnd) {
 
	if ((full_screen || this->fullscreen) && this->main_wnd) {
 
		DestroyWindow(this->main_wnd);
 
		this->main_wnd = 0;
 
	}
 

	
 
	if (full_screen) {
 
		DEVMODE settings;
 

	
 
		memset(&settings, 0, sizeof(settings));
 
		settings.dmSize = sizeof(settings);
 
		settings.dmFields =
 
			DM_BITSPERPEL |
 
			DM_PELSWIDTH |
 
			DM_PELSHEIGHT;
 
		settings.dmBitsPerPel = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
 
		settings.dmBitsPerPel = this->GetFullscreenBpp();
 
		settings.dmPelsWidth  = _wnd.width_org;
 
		settings.dmPelsHeight = _wnd.height_org;
 

	
 
		/* Check for 8 bpp support. */
 
		if (settings.dmBitsPerPel == 8 &&
 
				(_support8bpp != S8BPP_HARDWARE || ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL)) {
 
		if (settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
 
			settings.dmBitsPerPel = 32;
 
		}
 

	
 
		/* Test fullscreen with current resolution, if it fails use desktop resolution. */
 
		if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
 
			RECT r;
 
			GetWindowRect(GetDesktopWindow(), &r);
 
			/* Guard against recursion. If we already failed here once, just fall through to
 
			 * the next ChangeDisplaySettings call which will fail and error out appropriately. */
 
			if ((int)settings.dmPelsWidth != r.right - r.left || (int)settings.dmPelsHeight != r.bottom - r.top) {
 
				return this->ChangeResolution(r.right - r.left, r.bottom - r.top);
 
			}
 
		}
 

	
 
		if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
 
			this->MakeWindow(false);  // don't care about the result
 
			return false;  // the request failed
 
		}
 
	} else if (_wnd.fullscreen) {
 
	} else if (this->fullscreen) {
 
		/* restore display? */
 
		ChangeDisplaySettings(nullptr, 0);
 
		/* restore the resolution */
 
		_wnd.width = _bck_resolution.width;
 
		_wnd.height = _bck_resolution.height;
 
	}
 

	
 
	{
 
		RECT r;
 
		DWORD style, showstyle;
 
		int w, h;
 

	
 
		showstyle = SW_SHOWNORMAL;
 
		_wnd.fullscreen = full_screen;
 
		if (_wnd.fullscreen) {
 
		this->fullscreen = full_screen;
 
		if (this->fullscreen) {
 
			style = WS_POPUP;
 
			SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org);
 
		} else {
 
			style = WS_OVERLAPPEDWINDOW;
 
			/* On window creation, check if we were in maximize mode before */
 
			if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
 
			SetRect(&r, 0, 0, _wnd.width, _wnd.height);
 
		}
 

	
 
		AdjustWindowRect(&r, style, FALSE);
 
		w = r.right - r.left;
 
		h = r.bottom - r.top;
 

	
 
		if (this->main_wnd != nullptr) {
 
			if (!_window_maximize) SetWindowPos(this->main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
 
		} else {
 
			int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
 
			int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
 

	
 
			char window_title[64];
 
			seprintf(window_title, lastof(window_title), "OpenTTD %s", _openttd_revision);
 

	
 
			this->main_wnd = CreateWindow(_T("OTTD"), MB_TO_WIDE(window_title), style, x, y, w, h, 0, 0, GetModuleHandle(nullptr), this);
 
			if (this->main_wnd == nullptr) usererror("CreateWindow failed");
 
@@ -640,49 +645,49 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, U
 
				return 0;
 
			}
 

	
 
			/* Is the console key a dead key? If yes, ignore the first key down event. */
 
			if (HasBit(charcode, 31) && !console) {
 
				if (scancode == 41) {
 
					console = true;
 
					return 0;
 
				}
 
			}
 
			console = false;
 

	
 
			/* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
 
			 * clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */
 
			uint cur_keycode = keycode;
 
			keycode = 0;
 

	
 
			return HandleCharMsg(cur_keycode, LOWORD(charcode));
 
		}
 

	
 
		case WM_SYSKEYDOWN: // user presses F10 or Alt, both activating the title-menu
 
			switch (wParam) {
 
				case VK_RETURN:
 
				case 'F': // Full Screen on ALT + ENTER/F
 
					ToggleFullScreen(!_wnd.fullscreen);
 
					ToggleFullScreen(!video_driver->fullscreen);
 
					return 0;
 

	
 
				case VK_MENU: // Just ALT
 
					return 0; // do nothing
 

	
 
				case VK_F10: // F10, ignore activation of menu
 
					HandleKeypress(MapWindowsKey(wParam), 0);
 
					return 0;
 

	
 
				default: // ALT in combination with something else
 
					HandleKeypress(MapWindowsKey(wParam), 0);
 
					break;
 
			}
 
			break;
 

	
 
		case WM_SIZE:
 
			if (wParam != SIZE_MINIMIZED) {
 
				/* Set maximized flag when we maximize (obviously), but also when we
 
				 * switched to fullscreen from a maximized state */
 
				_window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
 
				if (_window_maximize || _fullscreen) _bck_resolution = _cur_resolution;
 
				video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
 
			}
 
			return 0;
 
@@ -759,49 +764,49 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, U
 
			if (delta < 0) {
 
				_cursor.wheel++;
 
			} else if (delta > 0) {
 
				_cursor.wheel--;
 
			}
 
			HandleMouseEvents();
 
			return 0;
 
		}
 

	
 
		case WM_SETFOCUS:
 
			_wnd.has_focus = true;
 
			SetCompositionPos(hwnd);
 
			break;
 

	
 
		case WM_KILLFOCUS:
 
			_wnd.has_focus = false;
 
			break;
 

	
 
		case WM_ACTIVATE: {
 
			/* Don't do anything if we are closing openttd */
 
			if (_exit_game) break;
 

	
 
			bool active = (LOWORD(wParam) != WA_INACTIVE);
 
			bool minimized = (HIWORD(wParam) != 0);
 
			if (_wnd.fullscreen) {
 
			if (video_driver->fullscreen) {
 
				if (active && minimized) {
 
					/* Restore the game window */
 
					ShowWindow(hwnd, SW_RESTORE);
 
					video_driver->MakeWindow(true);
 
				} else if (!active && !minimized) {
 
					/* Minimise the window and restore desktop */
 
					ShowWindow(hwnd, SW_MINIMIZE);
 
					ChangeDisplaySettings(nullptr, 0);
 
				}
 
			}
 
			break;
 
		}
 
	}
 

	
 
	return DefWindowProc(hwnd, msg, wParam, lParam);
 
}
 

	
 
static void RegisterWndClass()
 
{
 
	static bool registered = false;
 

	
 
	if (registered) return;
 

	
 
	HINSTANCE hinst = GetModuleHandle(nullptr);
 
@@ -815,75 +820,90 @@ static void RegisterWndClass()
 
		LoadCursor(nullptr, IDC_ARROW),
 
		0,
 
		0,
 
		_T("OTTD")
 
	};
 

	
 
	registered = true;
 
	if (!RegisterClass(&wnd)) usererror("RegisterClass failed");
 
}
 

	
 
static const Dimension default_resolutions[] = {
 
	{  640,  480 },
 
	{  800,  600 },
 
	{ 1024,  768 },
 
	{ 1152,  864 },
 
	{ 1280,  800 },
 
	{ 1280,  960 },
 
	{ 1280, 1024 },
 
	{ 1400, 1050 },
 
	{ 1600, 1200 },
 
	{ 1680, 1050 },
 
	{ 1920, 1200 }
 
};
 

	
 
static void FindResolutions()
 
static void FindResolutions(uint8 bpp)
 
{
 
	uint i;
 
	DEVMODEA dm;
 

	
 
	/* Check modes for the relevant fullscreen bpp */
 
	uint bpp = _support8bpp != S8BPP_HARDWARE ? 32 : BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
 

	
 
	_resolutions.clear();
 

	
 
	/* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95
 
	 * Doesn't really matter since we don't pass a string anyways, but still
 
	 * a letdown */
 
	for (i = 0; EnumDisplaySettingsA(nullptr, i, &dm) != 0; i++) {
 
	DEVMODE dm;
 
	for (uint i = 0; EnumDisplaySettings(nullptr, i, &dm) != 0; i++) {
 
		if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480) continue;
 
		if (std::find(_resolutions.begin(), _resolutions.end(), Dimension(dm.dmPelsWidth, dm.dmPelsHeight)) != _resolutions.end()) continue;
 
		_resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
 
	}
 

	
 
	/* We have found no resolutions, show the default list */
 
	if (_resolutions.empty()) {
 
		_resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
 
	}
 

	
 
	SortResolutions();
 
}
 

	
 
void VideoDriver_Win32Base::Initialize()
 
{
 
	this->UpdateAutoResolution();
 

	
 
	memset(&_wnd, 0, sizeof(_wnd));
 

	
 
	RegisterWndClass();
 
	FindResolutions(this->GetFullscreenBpp());
 

	
 
	/* fullscreen uses those */
 
	_wnd.width  = _wnd.width_org  = _cur_resolution.width;
 
	_wnd.height = _wnd.height_org = _cur_resolution.height;
 

	
 
	DEBUG(driver, 2, "Resolution for display: %ux%u", _cur_resolution.width, _cur_resolution.height);
 
}
 

	
 
void VideoDriver_Win32Base::Stop()
 
{
 
	DestroyWindow(this->main_wnd);
 

	
 
	if (this->fullscreen) ChangeDisplaySettings(nullptr, 0);
 
	MyShowCursor(true);
 
}
 
void VideoDriver_Win32Base::MakeDirty(int left, int top, int width, int height)
 
{
 
	Rect r = {left, top, left + width, top + height};
 
	_dirty_rect = BoundingRect(_dirty_rect, r);
 
}
 

	
 
void VideoDriver_Win32Base::CheckPaletteAnim()
 
{
 
	if (_cur_palette.count_dirty == 0) return;
 

	
 
	_local_palette = _cur_palette;
 
	this->MakeDirty(0, 0, _screen.width, _screen.height);
 
}
 

	
 
void VideoDriver_Win32Base::InputLoop()
 
{
 
	bool old_ctrl_pressed = _ctrl_pressed;
 

	
 
	_ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL) < 0;
 
	_shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0;
 

	
 
#if defined(_DEBUG)
 
	if (_shift_pressed)
 
#else
 
@@ -1088,82 +1108,67 @@ float VideoDriver_Win32Base::GetDPIScale
 
		cur_dpi = _GetDpiForSystem();
 
	}
 

	
 
	return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f; // Default Windows DPI value is 96.
 
}
 

	
 
bool VideoDriver_Win32Base::LockVideoBuffer()
 
{
 
	if (_draw_threaded) this->draw_lock.lock();
 
	return true;
 
}
 

	
 
void VideoDriver_Win32Base::UnlockVideoBuffer()
 
{
 
	if (_draw_threaded) this->draw_lock.unlock();
 
}
 

	
 

	
 
static FVideoDriver_Win32GDI iFVideoDriver_Win32GDI;
 

	
 
const char *VideoDriver_Win32GDI::Start(const StringList &param)
 
{
 
	if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return "Only real blitters supported";
 

	
 
	this->UpdateAutoResolution();
 

	
 
	memset(&_wnd, 0, sizeof(_wnd));
 

	
 
	RegisterWndClass();
 
	this->Initialize();
 

	
 
	this->MakePalette();
 

	
 
	FindResolutions();
 

	
 
	DEBUG(driver, 2, "Resolution for display: %ux%u", _cur_resolution.width, _cur_resolution.height);
 

	
 
	/* fullscreen uses those */
 
	_wnd.width_org = _cur_resolution.width;
 
	_wnd.height_org = _cur_resolution.height;
 

	
 
	this->AllocateBackingStore(_cur_resolution.width, _cur_resolution.height);
 
	this->MakeWindow(_fullscreen);
 

	
 
	MarkWholeScreenDirty();
 

	
 
	_draw_threaded = !GetDriverParam(param, "no_threads") && !GetDriverParam(param, "no_thread") && std::thread::hardware_concurrency() > 1;
 

	
 
	return nullptr;
 
}
 

	
 
void VideoDriver_Win32GDI::Stop()
 
{
 
	DeleteObject(this->gdi_palette);
 
	DeleteObject(this->dib_sect);
 
	DestroyWindow(this->main_wnd);
 

	
 
	if (_wnd.fullscreen) ChangeDisplaySettings(nullptr, 0);
 
	MyShowCursor(true);
 
	this->VideoDriver_Win32Base::Stop();
 
}
 

	
 
bool VideoDriver_Win32GDI::AllocateBackingStore(int w, int h, bool force)
 
{
 
	uint bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
 

	
 
	w = std::max(w, 64);
 
	h = std::max(h, 64);
 

	
 
	if (!force && w == _screen.width && h == _screen.height) return false;
 

	
 
	BITMAPINFO *bi = (BITMAPINFO *)alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);
 
	memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);
 
	bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 

	
 
	bi->bmiHeader.biWidth = _wnd.width = w;
 
	bi->bmiHeader.biHeight = -(_wnd.height = h);
 

	
 
	bi->bmiHeader.biPlanes = 1;
 
	bi->bmiHeader.biBitCount = bpp;
 
	bi->bmiHeader.biCompression = BI_RGB;
 

	
 
	if (this->dib_sect) DeleteObject(this->dib_sect);
 

	
src/video/win32_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 win32_v.h Base of the Windows video driver. */
 

	
 
#ifndef VIDEO_WIN32_H
 
#define VIDEO_WIN32_H
 

	
 
#include "video_driver.hpp"
 

	
 
/** Base class for Windows video drivers. */
 
class VideoDriver_Win32Base : public VideoDriver {
 
public:
 
	VideoDriver_Win32Base() : main_wnd(nullptr) {}
 
	VideoDriver_Win32Base() : main_wnd(nullptr), fullscreen(false) {}
 

	
 
	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;
 

	
 
	void AcquireBlitterLock() override;
 

	
 
	void ReleaseBlitterLock() override;
 

	
 
	bool ClaimMousePointer() override;
 

	
 
	void EditBoxLostFocus() override;
 

	
 
protected:
 
	HWND    main_wnd;      ///< Handle to system window.
 
	bool    fullscreen;    ///< Whether to use (true) fullscreen mode.
 

	
 
	Dimension GetScreenSize() const override;
 
	float GetDPIScale() override;
 
	void InputLoop() override;
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void CheckPaletteAnim() override;
 

	
 
	void Initialize();
 
	bool MakeWindow(bool full_screen);
 
	virtual uint8 GetFullscreenBpp();
 

	
 
	/** (Re-)create the backing store. */
 
	virtual bool AllocateBackingStore(int w, int h, bool force = false) = 0;
 
	/** Palette of the window has changed. */
 
	virtual void PaletteChanged(HWND hWnd) = 0;
 

	
 
private:
 
	std::unique_lock<std::recursive_mutex> draw_lock;
 

	
 
	void ClientSizeChanged(int w, int h);
 

	
 
	static void PaintThreadThunk(VideoDriver_Win32Base *drv);
 

	
 
	friend LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
};
 
/** The GDI video driver for windows. */
 
class VideoDriver_Win32GDI : public VideoDriver_Win32Base {
 
public:
 
	VideoDriver_Win32GDI() : dib_sect(nullptr), gdi_palette(nullptr) {}
 

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

	
 
	void Stop() override;
 

	
0 comments (0 inline, 0 general)