|
@@ -11,12 +11,13 @@
|
|
|
#include "../openttd.h"
|
|
|
#include "../gfx_func.h"
|
|
|
#include "../os/windows/win32.h"
|
|
|
#include "../rev.h"
|
|
|
#include "../blitter/factory.hpp"
|
|
|
#include "../network/network.h"
|
|
|
#include "../core/geometry_func.hpp"
|
|
|
#include "../core/math_func.hpp"
|
|
|
#include "../core/random_func.hpp"
|
|
|
#include "../texteff.hpp"
|
|
|
#include "../thread.h"
|
|
|
#include "../progress.h"
|
|
|
#include "../window_gui.h"
|
|
@@ -41,13 +42,12 @@
|
|
|
|
|
|
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.
|
|
|
RECT update_rect; ///< Current dirty rect.
|
|
|
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?
|
|
@@ -67,12 +67,14 @@ static std::recursive_mutex *_draw_mutex
|
|
|
/** 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;
|
|
|
|
|
|
static void MakePalette()
|
|
|
{
|
|
|
_cur_palette.first_dirty = 0;
|
|
|
_cur_palette.count_dirty = 256;
|
|
|
_local_palette = _cur_palette;
|
|
@@ -327,16 +329,30 @@ bool VideoDriver_Win32::MakeWindow(bool
|
|
|
|
|
|
GameSizeChanged(); // invalidate all windows, force redraw
|
|
|
return true; // the request succeeded
|
|
|
}
|
|
|
|
|
|
/** Do palette animation and blit to the window. */
|
|
|
static void PaintWindow(HDC dc)
|
|
|
static void PaintWindow()
|
|
|
{
|
|
|
PerformanceMeasurer framerate(PFE_VIDEO);
|
|
|
|
|
|
if (IsEmptyRect(_dirty_rect)) return;
|
|
|
|
|
|
/* Convert update region from logical to device coordinates. */
|
|
|
POINT pt = {0, 0};
|
|
|
ClientToScreen(_wnd.main_wnd, &pt);
|
|
|
|
|
|
RECT r = { _dirty_rect.left, _dirty_rect.top, _dirty_rect.right, _dirty_rect.bottom };
|
|
|
OffsetRect(&r, pt.x, pt.y);
|
|
|
|
|
|
/* Create a device context that is clipped to the region we need to draw.
|
|
|
* GetDCEx 'consumes' the update region, so we may not destroy it ourself. */
|
|
|
HRGN rgn = CreateRectRgnIndirect(&r);
|
|
|
HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
|
|
|
|
|
|
HDC dc2 = CreateCompatibleDC(dc);
|
|
|
HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
|
|
|
HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
|
|
|
|
|
|
if (_cur_palette.count_dirty != 0) {
|
|
|
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
|
@@ -360,39 +376,29 @@ static void PaintWindow(HDC dc)
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
|
MemSetT(&_dirty_rect, 0);
|
|
|
}
|
|
|
|
|
|
static void PaintWindowThread()
|
|
|
{
|
|
|
/* First tell the main thread we're started */
|
|
|
std::unique_lock<std::recursive_mutex> lock(*_draw_mutex);
|
|
|
_draw_signal->notify_one();
|
|
|
|
|
|
/* Now wait for the first thing to draw! */
|
|
|
_draw_signal->wait(*_draw_mutex);
|
|
|
|
|
|
while (_draw_continue) {
|
|
|
/* Convert update region from logical to device coordinates. */
|
|
|
POINT pt = {0, 0};
|
|
|
ClientToScreen(_wnd.main_wnd, &pt);
|
|
|
OffsetRect(&_wnd.update_rect, pt.x, pt.y);
|
|
|
|
|
|
/* Create a device context that is clipped to the region we need to draw.
|
|
|
* GetDCEx 'consumes' the update region, so we may not destroy it ourself. */
|
|
|
HRGN rgn = CreateRectRgnIndirect(&_wnd.update_rect);
|
|
|
HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
|
|
|
|
|
|
PaintWindow(dc);
|
|
|
|
|
|
/* Clear update rect. */
|
|
|
SetRectEmpty(&_wnd.update_rect);
|
|
|
ReleaseDC(_wnd.main_wnd, dc);
|
|
|
PaintWindow();
|
|
|
|
|
|
/* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
|
|
|
GdiFlush();
|
|
|
|
|
|
_draw_signal->wait(*_draw_mutex);
|
|
|
}
|
|
@@ -598,60 +604,43 @@ static void CancelIMEComposition(HWND hw
|
|
|
}
|
|
|
|
|
|
static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
{
|
|
|
static uint32 keycode = 0;
|
|
|
static bool console = false;
|
|
|
static bool in_sizemove = false;
|
|
|
|
|
|
switch (msg) {
|
|
|
case WM_CREATE:
|
|
|
_cursor.in_window = false; // Win32 has mouse tracking.
|
|
|
SetCompositionPos(hwnd);
|
|
|
_imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
|
|
|
break;
|
|
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
|
in_sizemove = true;
|
|
|
break;
|
|
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
|
in_sizemove = false;
|
|
|
break;
|
|
|
case WM_PAINT: {
|
|
|
RECT r;
|
|
|
GetUpdateRect(hwnd, &r, FALSE);
|
|
|
static_cast<VideoDriver_Win32 *>(VideoDriver::GetInstance())->MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
|
|
|
|
|
|
case WM_PAINT:
|
|
|
if (!in_sizemove && _draw_mutex != nullptr && !HasModalProgress()) {
|
|
|
/* Get the union of the old update rect and the new update rect. */
|
|
|
RECT r;
|
|
|
GetUpdateRect(hwnd, &r, FALSE);
|
|
|
UnionRect(&_wnd.update_rect, &_wnd.update_rect, &r);
|
|
|
|
|
|
/* Mark the window as updated, otherwise Windows would send more WM_PAINT messages. */
|
|
|
ValidateRect(hwnd, nullptr);
|
|
|
_draw_signal->notify_one();
|
|
|
} else {
|
|
|
PAINTSTRUCT ps;
|
|
|
|
|
|
BeginPaint(hwnd, &ps);
|
|
|
PaintWindow(ps.hdc);
|
|
|
EndPaint(hwnd, &ps);
|
|
|
}
|
|
|
ValidateRect(hwnd, nullptr);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
case WM_PALETTECHANGED:
|
|
|
if ((HWND)wParam == hwnd) return 0;
|
|
|
FALLTHROUGH;
|
|
|
|
|
|
case WM_QUERYNEWPALETTE: {
|
|
|
HDC hDC = GetWindowDC(hwnd);
|
|
|
HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
|
|
|
UINT nChanged = RealizePalette(hDC);
|
|
|
|
|
|
SelectPalette(hDC, hOldPalette, TRUE);
|
|
|
ReleaseDC(hwnd, hDC);
|
|
|
if (nChanged != 0) InvalidateRect(hwnd, nullptr, FALSE);
|
|
|
if (nChanged != 0) {
|
|
|
static_cast<VideoDriver_Win32 *>(VideoDriver::GetInstance())->MakeDirty(0, 0, _screen.width, _screen.height);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
case WM_CLOSE:
|
|
|
HandleExitGameRequest();
|
|
|
return 0;
|
|
@@ -1114,23 +1103,22 @@ void VideoDriver_Win32::Stop()
|
|
|
if (_wnd.fullscreen) ChangeDisplaySettings(nullptr, 0);
|
|
|
MyShowCursor(true);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32::MakeDirty(int left, int top, int width, int height)
|
|
|
{
|
|
|
RECT r = { left, top, left + width, top + height };
|
|
|
|
|
|
InvalidateRect(_wnd.main_wnd, &r, FALSE);
|
|
|
Rect r = {left, top, left + width, top + height};
|
|
|
_dirty_rect = BoundingRect(_dirty_rect, r);
|
|
|
}
|
|
|
|
|
|
static void CheckPaletteAnim()
|
|
|
void VideoDriver_Win32::CheckPaletteAnim()
|
|
|
{
|
|
|
if (_cur_palette.count_dirty == 0) return;
|
|
|
|
|
|
_local_palette = _cur_palette;
|
|
|
InvalidateRect(_wnd.main_wnd, nullptr, FALSE);
|
|
|
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32::MainLoop()
|
|
|
{
|
|
|
MSG mesg;
|
|
|
auto cur_ticks = std::chrono::steady_clock::now();
|
|
@@ -1254,12 +1242,18 @@ void VideoDriver_Win32::MainLoop()
|
|
|
/* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
|
|
|
GdiFlush();
|
|
|
|
|
|
InputLoop();
|
|
|
UpdateWindows();
|
|
|
CheckPaletteAnim();
|
|
|
|
|
|
if (_draw_mutex != nullptr && !HasModalProgress()) {
|
|
|
_draw_signal->notify_one();
|
|
|
} else {
|
|
|
PaintWindow();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
|
|
|
if (!_fast_forward || _pause_mode) {
|
|
|
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
|
|
auto next_tick = std::min(next_draw_tick, next_game_tick);
|