|
@@ -38,16 +38,13 @@
|
|
|
|
|
|
#ifndef PM_QS_INPUT
|
|
|
#define PM_QS_INPUT 0x20000
|
|
|
#endif
|
|
|
|
|
|
static struct {
|
|
|
HWND main_wnd; ///< Handle to system window.
|
|
|
HBITMAP dib_sect; ///< System bitmap object referencing our rendering buffer.
|
|
|
void *buffer_bits; ///< Internal rendering buffer.
|
|
|
HPALETTE gdi_palette; ///< Palette object for 8bpp blitter.
|
|
|
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?
|
|
@@ -68,49 +65,12 @@ static std::condition_variable_any *_dra
|
|
|
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;
|
|
|
|
|
|
static void MakePalette()
|
|
|
{
|
|
|
_cur_palette.first_dirty = 0;
|
|
|
_cur_palette.count_dirty = 256;
|
|
|
_local_palette = _cur_palette;
|
|
|
|
|
|
LOGPALETTE *pal = (LOGPALETTE*)alloca(sizeof(LOGPALETTE) + (256 - 1) * sizeof(PALETTEENTRY));
|
|
|
|
|
|
pal->palVersion = 0x300;
|
|
|
pal->palNumEntries = 256;
|
|
|
|
|
|
for (uint i = 0; i != 256; i++) {
|
|
|
pal->palPalEntry[i].peRed = _local_palette.palette[i].r;
|
|
|
pal->palPalEntry[i].peGreen = _local_palette.palette[i].g;
|
|
|
pal->palPalEntry[i].peBlue = _local_palette.palette[i].b;
|
|
|
pal->palPalEntry[i].peFlags = 0;
|
|
|
|
|
|
}
|
|
|
_wnd.gdi_palette = CreatePalette(pal);
|
|
|
if (_wnd.gdi_palette == nullptr) usererror("CreatePalette failed!\n");
|
|
|
}
|
|
|
|
|
|
static void UpdatePalette(HDC dc, uint start, uint count)
|
|
|
{
|
|
|
RGBQUAD rgb[256];
|
|
|
uint i;
|
|
|
|
|
|
for (i = 0; i != count; i++) {
|
|
|
rgb[i].rgbRed = _local_palette.palette[start + i].r;
|
|
|
rgb[i].rgbGreen = _local_palette.palette[start + i].g;
|
|
|
rgb[i].rgbBlue = _local_palette.palette[start + i].b;
|
|
|
rgb[i].rgbReserved = 0;
|
|
|
}
|
|
|
|
|
|
SetDIBColorTable(dc, start, count, rgb);
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_Win32Base::ClaimMousePointer()
|
|
|
{
|
|
|
MyShowCursor(false, true);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
@@ -179,54 +139,27 @@ static uint MapWindowsKey(uint sym)
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
/* Keep this function here..
|
|
|
* It allows you to redraw the screen from within the MSVC debugger */
|
|
|
int RedrawScreenDebug()
|
|
|
{
|
|
|
HDC dc, dc2;
|
|
|
static int _fooctr;
|
|
|
HBITMAP old_bmp;
|
|
|
HPALETTE old_palette;
|
|
|
|
|
|
UpdateWindows();
|
|
|
|
|
|
dc = GetDC(_wnd.main_wnd);
|
|
|
dc2 = CreateCompatibleDC(dc);
|
|
|
|
|
|
old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
|
|
|
old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
|
|
|
BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
|
|
|
SelectPalette(dc, old_palette, TRUE);
|
|
|
SelectObject(dc2, old_bmp);
|
|
|
DeleteDC(dc2);
|
|
|
ReleaseDC(_wnd.main_wnd, dc);
|
|
|
|
|
|
return _fooctr++;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
/**
|
|
|
* 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) && _wnd.main_wnd) {
|
|
|
DestroyWindow(_wnd.main_wnd);
|
|
|
_wnd.main_wnd = 0;
|
|
|
if ((full_screen || _wnd.fullscreen) && this->main_wnd) {
|
|
|
DestroyWindow(this->main_wnd);
|
|
|
this->main_wnd = 0;
|
|
|
}
|
|
|
|
|
|
if (full_screen) {
|
|
|
DEVMODE settings;
|
|
|
|
|
|
memset(&settings, 0, sizeof(settings));
|
|
@@ -286,31 +219,31 @@ bool VideoDriver_Win32Base::MakeWindow(b
|
|
|
}
|
|
|
|
|
|
AdjustWindowRect(&r, style, FALSE);
|
|
|
w = r.right - r.left;
|
|
|
h = r.bottom - r.top;
|
|
|
|
|
|
if (_wnd.main_wnd != nullptr) {
|
|
|
if (!_window_maximize) SetWindowPos(_wnd.main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
|
|
|
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);
|
|
|
|
|
|
_wnd.main_wnd = CreateWindow(_T("OTTD"), MB_TO_WIDE(window_title), style, x, y, w, h, 0, 0, GetModuleHandle(nullptr), this);
|
|
|
if (_wnd.main_wnd == nullptr) usererror("CreateWindow failed");
|
|
|
ShowWindow(_wnd.main_wnd, showstyle);
|
|
|
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");
|
|
|
ShowWindow(this->main_wnd, showstyle);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
BlitterFactory::GetCurrentBlitter()->PostResize();
|
|
|
|
|
|
GameSizeChanged(); // invalidate all windows, force redraw
|
|
|
return true; // the request succeeded
|
|
|
GameSizeChanged();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/* static */ void VideoDriver_Win32Base::PaintThreadThunk(VideoDriver_Win32Base *drv)
|
|
|
{
|
|
|
drv->PaintThread();
|
|
|
}
|
|
@@ -1074,13 +1007,13 @@ void VideoDriver_Win32Base::ClientSizeCh
|
|
|
|
|
|
bool VideoDriver_Win32Base::ChangeResolution(int w, int h)
|
|
|
{
|
|
|
std::unique_lock<std::recursive_mutex> lock;
|
|
|
if (_draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
|
|
|
|
|
|
if (_window_maximize) ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL);
|
|
|
if (_window_maximize) ShowWindow(this->main_wnd, SW_SHOWNORMAL);
|
|
|
|
|
|
_wnd.width = _wnd.width_org = w;
|
|
|
_wnd.height = _wnd.height_org = h;
|
|
|
|
|
|
return this->MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching
|
|
|
}
|
|
@@ -1105,15 +1038,15 @@ void VideoDriver_Win32Base::ReleaseBlitt
|
|
|
|
|
|
void VideoDriver_Win32Base::EditBoxLostFocus()
|
|
|
{
|
|
|
std::unique_lock<std::recursive_mutex> lock;
|
|
|
if (_draw_mutex != nullptr) lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
|
|
|
|
|
|
CancelIMEComposition(_wnd.main_wnd);
|
|
|
SetCompositionPos(_wnd.main_wnd);
|
|
|
SetCandidatePos(_wnd.main_wnd);
|
|
|
CancelIMEComposition(this->main_wnd);
|
|
|
SetCompositionPos(this->main_wnd);
|
|
|
SetCandidatePos(this->main_wnd);
|
|
|
}
|
|
|
|
|
|
Dimension VideoDriver_Win32Base::GetScreenSize() const
|
|
|
{
|
|
|
return { static_cast<uint>(GetSystemMetrics(SM_CXSCREEN)), static_cast<uint>(GetSystemMetrics(SM_CYSCREEN)) };
|
|
|
}
|
|
@@ -1136,20 +1069,20 @@ float VideoDriver_Win32Base::GetDPIScale
|
|
|
_GetDpiForSystem = (PFNGETDPIFORSYSTEM)GetProcAddress(GetModuleHandle(_T("User32")), "GetDpiForSystem");
|
|
|
_GetDpiForMonitor = (PFNGETDPIFORMONITOR)GetProcAddress(LoadLibrary(_T("Shcore.dll")), "GetDpiForMonitor");
|
|
|
}
|
|
|
|
|
|
UINT cur_dpi = 0;
|
|
|
|
|
|
if (cur_dpi == 0 && _GetDpiForWindow != nullptr && _wnd.main_wnd != nullptr) {
|
|
|
if (cur_dpi == 0 && _GetDpiForWindow != nullptr && this->main_wnd != nullptr) {
|
|
|
/* Per window DPI is supported since Windows 10 Ver 1607. */
|
|
|
cur_dpi = _GetDpiForWindow(_wnd.main_wnd);
|
|
|
cur_dpi = _GetDpiForWindow(this->main_wnd);
|
|
|
}
|
|
|
if (cur_dpi == 0 && _GetDpiForMonitor != nullptr && _wnd.main_wnd != nullptr) {
|
|
|
if (cur_dpi == 0 && _GetDpiForMonitor != nullptr && this->main_wnd != nullptr) {
|
|
|
/* Per monitor is supported since Windows 8.1. */
|
|
|
UINT dpiX, dpiY;
|
|
|
if (SUCCEEDED(_GetDpiForMonitor(MonitorFromWindow(_wnd.main_wnd, MONITOR_DEFAULTTOPRIMARY), 0 /* MDT_EFFECTIVE_DPI */, &dpiX, &dpiY))) {
|
|
|
if (SUCCEEDED(_GetDpiForMonitor(MonitorFromWindow(this->main_wnd, MONITOR_DEFAULTTOPRIMARY), 0 /* MDT_EFFECTIVE_DPI */, &dpiX, &dpiY))) {
|
|
|
cur_dpi = dpiX; // X and Y are always identical.
|
|
|
}
|
|
|
}
|
|
|
if (cur_dpi == 0 && _GetDpiForSystem != nullptr) {
|
|
|
/* Fall back to system DPI. */
|
|
|
cur_dpi = _GetDpiForSystem();
|
|
@@ -1179,13 +1112,13 @@ const char *VideoDriver_Win32GDI::Start(
|
|
|
this->UpdateAutoResolution();
|
|
|
|
|
|
memset(&_wnd, 0, sizeof(_wnd));
|
|
|
|
|
|
RegisterWndClass();
|
|
|
|
|
|
MakePalette();
|
|
|
this->MakePalette();
|
|
|
|
|
|
FindResolutions();
|
|
|
|
|
|
DEBUG(driver, 2, "Resolution for display: %ux%u", _cur_resolution.width, _cur_resolution.height);
|
|
|
|
|
|
/* fullscreen uses those */
|
|
@@ -1201,15 +1134,15 @@ const char *VideoDriver_Win32GDI::Start(
|
|
|
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::Stop()
|
|
|
{
|
|
|
DeleteObject(_wnd.gdi_palette);
|
|
|
DeleteObject(_wnd.dib_sect);
|
|
|
DestroyWindow(_wnd.main_wnd);
|
|
|
DeleteObject(this->gdi_palette);
|
|
|
DeleteObject(this->dib_sect);
|
|
|
DestroyWindow(this->main_wnd);
|
|
|
|
|
|
if (_wnd.fullscreen) ChangeDisplaySettings(nullptr, 0);
|
|
|
MyShowCursor(true);
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_Win32GDI::AllocateBackingStore(int w, int h, bool force)
|
|
@@ -1229,17 +1162,17 @@ bool VideoDriver_Win32GDI::AllocateBacki
|
|
|
bi->bmiHeader.biHeight = -(_wnd.height = h);
|
|
|
|
|
|
bi->bmiHeader.biPlanes = 1;
|
|
|
bi->bmiHeader.biBitCount = bpp;
|
|
|
bi->bmiHeader.biCompression = BI_RGB;
|
|
|
|
|
|
if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
|
|
|
if (this->dib_sect) DeleteObject(this->dib_sect);
|
|
|
|
|
|
HDC dc = GetDC(0);
|
|
|
_wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&_wnd.buffer_bits, nullptr, 0);
|
|
|
if (_wnd.dib_sect == nullptr) usererror("CreateDIBSection failed");
|
|
|
this->dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&_wnd.buffer_bits, nullptr, 0);
|
|
|
if (this->dib_sect == nullptr) usererror("CreateDIBSection failed");
|
|
|
ReleaseDC(0, dc);
|
|
|
|
|
|
_screen.width = w;
|
|
|
_screen.pitch = (bpp == 8) ? Align(w, 4) : w;
|
|
|
_screen.height = h;
|
|
|
_screen.dst_ptr = _wnd.buffer_bits;
|
|
@@ -1250,16 +1183,52 @@ bool VideoDriver_Win32GDI::AllocateBacki
|
|
|
bool VideoDriver_Win32GDI::AfterBlitterChange()
|
|
|
{
|
|
|
assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0);
|
|
|
return this->AllocateBackingStore(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::MakePalette()
|
|
|
{
|
|
|
_cur_palette.first_dirty = 0;
|
|
|
_cur_palette.count_dirty = 256;
|
|
|
_local_palette = _cur_palette;
|
|
|
|
|
|
LOGPALETTE *pal = (LOGPALETTE*)alloca(sizeof(LOGPALETTE) + (256 - 1) * sizeof(PALETTEENTRY));
|
|
|
|
|
|
pal->palVersion = 0x300;
|
|
|
pal->palNumEntries = 256;
|
|
|
|
|
|
for (uint i = 0; i != 256; i++) {
|
|
|
pal->palPalEntry[i].peRed = _local_palette.palette[i].r;
|
|
|
pal->palPalEntry[i].peGreen = _local_palette.palette[i].g;
|
|
|
pal->palPalEntry[i].peBlue = _local_palette.palette[i].b;
|
|
|
pal->palPalEntry[i].peFlags = 0;
|
|
|
|
|
|
}
|
|
|
this->gdi_palette = CreatePalette(pal);
|
|
|
if (this->gdi_palette == nullptr) usererror("CreatePalette failed!\n");
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::UpdatePalette(HDC dc, uint start, uint count)
|
|
|
{
|
|
|
RGBQUAD rgb[256];
|
|
|
|
|
|
for (uint i = 0; i != count; i++) {
|
|
|
rgb[i].rgbRed = _local_palette.palette[start + i].r;
|
|
|
rgb[i].rgbGreen = _local_palette.palette[start + i].g;
|
|
|
rgb[i].rgbBlue = _local_palette.palette[start + i].b;
|
|
|
rgb[i].rgbReserved = 0;
|
|
|
}
|
|
|
|
|
|
SetDIBColorTable(dc, start, count, rgb);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::PaletteChanged(HWND hWnd)
|
|
|
{
|
|
|
HDC hDC = GetWindowDC(hWnd);
|
|
|
HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
|
|
|
HPALETTE hOldPalette = SelectPalette(hDC, this->gdi_palette, FALSE);
|
|
|
UINT nChanged = RealizePalette(hDC);
|
|
|
|
|
|
SelectPalette(hDC, hOldPalette, TRUE);
|
|
|
ReleaseDC(hWnd, hDC);
|
|
|
if (nChanged != 0) this->MakeDirty(0, 0, _screen.width, _screen.height);
|
|
|
}
|
|
@@ -1267,24 +1236,24 @@ void VideoDriver_Win32GDI::PaletteChange
|
|
|
void VideoDriver_Win32GDI::Paint()
|
|
|
{
|
|
|
PerformanceMeasurer framerate(PFE_VIDEO);
|
|
|
|
|
|
if (IsEmptyRect(_dirty_rect)) return;
|
|
|
|
|
|
HDC dc = GetDC(_wnd.main_wnd);
|
|
|
HDC dc = GetDC(this->main_wnd);
|
|
|
HDC dc2 = CreateCompatibleDC(dc);
|
|
|
|
|
|
HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
|
|
|
HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
|
|
|
HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, this->dib_sect);
|
|
|
HPALETTE old_palette = SelectPalette(dc, this->gdi_palette, FALSE);
|
|
|
|
|
|
if (_cur_palette.count_dirty != 0) {
|
|
|
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
|
|
|
|
|
switch (blitter->UsePaletteAnimation()) {
|
|
|
case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
|
|
|
UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
|
|
|
this->UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
|
|
|
break;
|
|
|
|
|
|
case Blitter::PALETTE_ANIMATION_BLITTER:
|
|
|
blitter->PaletteAnimate(_local_palette);
|
|
|
break;
|
|
|
|
|
@@ -1299,13 +1268,13 @@ void VideoDriver_Win32GDI::Paint()
|
|
|
|
|
|
BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
|
|
|
SelectPalette(dc, old_palette, TRUE);
|
|
|
SelectObject(dc2, old_bmp);
|
|
|
DeleteDC(dc2);
|
|
|
|
|
|
ReleaseDC(_wnd.main_wnd, dc);
|
|
|
ReleaseDC(this->main_wnd, dc);
|
|
|
|
|
|
_dirty_rect = {};
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::PaintThread()
|
|
|
{
|
|
@@ -1322,6 +1291,26 @@ void VideoDriver_Win32GDI::PaintThread()
|
|
|
/* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
|
|
|
GdiFlush();
|
|
|
|
|
|
_draw_signal->wait(*_draw_mutex);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
/* Keep this function here..
|
|
|
* It allows you to redraw the screen from within the MSVC debugger */
|
|
|
/* static */ int VideoDriver_Win32GDI::RedrawScreenDebug()
|
|
|
{
|
|
|
static int _fooctr;
|
|
|
|
|
|
_screen.dst_ptr = _wnd.buffer_bits;
|
|
|
UpdateWindows();
|
|
|
|
|
|
VideoDriver_Win32GDI *drv = static_cast<VideoDriver_Win32GDI *>(VideoDriver::GetInstance());
|
|
|
|
|
|
drv->Paint();
|
|
|
GdiFlush();
|
|
|
|
|
|
return _fooctr++;
|
|
|
}
|
|
|
#endif
|