|
@@ -179,29 +179,12 @@ 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;
|
|
|
}
|
|
|
|
|
|
static bool AllocateDibSection(int w, int h, bool force = false);
|
|
|
|
|
|
static void ClientSizeChanged(int w, int h)
|
|
|
{
|
|
|
/* allocate new dib section of the new size */
|
|
|
if (AllocateDibSection(w, h)) {
|
|
|
/* mark all palette colours dirty */
|
|
|
_cur_palette.first_dirty = 0;
|
|
|
_cur_palette.count_dirty = 256;
|
|
|
_local_palette = _cur_palette;
|
|
|
|
|
|
BlitterFactory::GetCurrentBlitter()->PostResize();
|
|
|
|
|
|
GameSizeChanged();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
/* Keep this function here..
|
|
|
* It allows you to redraw the screen from within the MSVC debugger */
|
|
|
int RedrawScreenDebug()
|
|
|
{
|
|
|
HDC dc, dc2;
|
|
@@ -324,75 +307,12 @@ bool VideoDriver_Win32Base::MakeWindow(b
|
|
|
BlitterFactory::GetCurrentBlitter()->PostResize();
|
|
|
|
|
|
GameSizeChanged(); // invalidate all windows, force redraw
|
|
|
return true; // the request succeeded
|
|
|
}
|
|
|
|
|
|
/** Do palette animation and blit to the window. */
|
|
|
void VideoDriver_Win32Base::Paint()
|
|
|
{
|
|
|
PerformanceMeasurer framerate(PFE_VIDEO);
|
|
|
|
|
|
if (IsEmptyRect(_dirty_rect)) return;
|
|
|
|
|
|
HDC dc = GetDC(_wnd.main_wnd);
|
|
|
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();
|
|
|
|
|
|
switch (blitter->UsePaletteAnimation()) {
|
|
|
case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
|
|
|
UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
|
|
|
break;
|
|
|
|
|
|
case Blitter::PALETTE_ANIMATION_BLITTER:
|
|
|
blitter->PaletteAnimate(_local_palette);
|
|
|
break;
|
|
|
|
|
|
case Blitter::PALETTE_ANIMATION_NONE:
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
NOT_REACHED();
|
|
|
}
|
|
|
_cur_palette.count_dirty = 0;
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
|
_dirty_rect = {};
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32Base::PaintThread()
|
|
|
{
|
|
|
/* 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) {
|
|
|
this->Paint();
|
|
|
|
|
|
/* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
|
|
|
GdiFlush();
|
|
|
|
|
|
_draw_signal->wait(*_draw_mutex);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* static */ void VideoDriver_Win32Base::PaintThreadThunk(VideoDriver_Win32Base *drv)
|
|
|
{
|
|
|
drv->PaintThread();
|
|
|
}
|
|
|
|
|
|
/** Forward key presses to the window system. */
|
|
@@ -619,24 +539,15 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, U
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
video_driver->MakeDirty(0, 0, _screen.width, _screen.height);
|
|
|
}
|
|
|
case WM_QUERYNEWPALETTE:
|
|
|
video_driver->PaletteChanged(hwnd);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
case WM_CLOSE:
|
|
|
HandleExitGameRequest();
|
|
|
return 0;
|
|
|
|
|
|
case WM_DESTROY:
|
|
@@ -836,13 +747,13 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, U
|
|
|
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;
|
|
|
ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
|
|
|
video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
|
|
|
}
|
|
|
return 0;
|
|
|
|
|
|
case WM_SIZING: {
|
|
|
RECT *r = (RECT*)lParam;
|
|
|
RECT r2;
|
|
@@ -975,49 +886,12 @@ static void RegisterWndClass()
|
|
|
};
|
|
|
|
|
|
registered = true;
|
|
|
if (!RegisterClass(&wnd)) usererror("RegisterClass failed");
|
|
|
}
|
|
|
|
|
|
static bool AllocateDibSection(int w, int h, bool force)
|
|
|
{
|
|
|
BITMAPINFO *bi;
|
|
|
HDC dc;
|
|
|
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;
|
|
|
|
|
|
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 (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
|
|
|
|
|
|
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");
|
|
|
ReleaseDC(0, dc);
|
|
|
|
|
|
_screen.width = w;
|
|
|
_screen.pitch = (bpp == 8) ? Align(w, 4) : w;
|
|
|
_screen.height = h;
|
|
|
_screen.dst_ptr = _wnd.buffer_bits;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static const Dimension default_resolutions[] = {
|
|
|
{ 640, 480 },
|
|
|
{ 800, 600 },
|
|
|
{ 1024, 768 },
|
|
|
{ 1152, 864 },
|
|
|
{ 1280, 800 },
|
|
@@ -1180,12 +1054,27 @@ void VideoDriver_Win32Base::MainLoop()
|
|
|
delete _draw_signal;
|
|
|
|
|
|
_draw_mutex = nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32Base::ClientSizeChanged(int w, int h)
|
|
|
{
|
|
|
/* Allocate backing store of the new size. */
|
|
|
if (this->AllocateBackingStore(w, h)) {
|
|
|
/* Mark all palette colours dirty. */
|
|
|
_cur_palette.first_dirty = 0;
|
|
|
_cur_palette.count_dirty = 256;
|
|
|
_local_palette = _cur_palette;
|
|
|
|
|
|
BlitterFactory::GetCurrentBlitter()->PostResize();
|
|
|
|
|
|
GameSizeChanged();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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);
|
|
@@ -1300,13 +1189,13 @@ const char *VideoDriver_Win32GDI::Start(
|
|
|
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;
|
|
|
|
|
|
AllocateDibSection(_cur_resolution.width, _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;
|
|
|
|
|
@@ -1320,11 +1209,119 @@ void VideoDriver_Win32GDI::Stop()
|
|
|
DestroyWindow(_wnd.main_wnd);
|
|
|
|
|
|
if (_wnd.fullscreen) ChangeDisplaySettings(nullptr, 0);
|
|
|
MyShowCursor(true);
|
|
|
}
|
|
|
|
|
|
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 (_wnd.dib_sect) DeleteObject(_wnd.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");
|
|
|
ReleaseDC(0, dc);
|
|
|
|
|
|
_screen.width = w;
|
|
|
_screen.pitch = (bpp == 8) ? Align(w, 4) : w;
|
|
|
_screen.height = h;
|
|
|
_screen.dst_ptr = _wnd.buffer_bits;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool VideoDriver_Win32GDI::AfterBlitterChange()
|
|
|
{
|
|
|
assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0);
|
|
|
return AllocateDibSection(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen);
|
|
|
return this->AllocateBackingStore(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::PaletteChanged(HWND hWnd)
|
|
|
{
|
|
|
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) this->MakeDirty(0, 0, _screen.width, _screen.height);
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::Paint()
|
|
|
{
|
|
|
PerformanceMeasurer framerate(PFE_VIDEO);
|
|
|
|
|
|
if (IsEmptyRect(_dirty_rect)) return;
|
|
|
|
|
|
HDC dc = GetDC(_wnd.main_wnd);
|
|
|
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();
|
|
|
|
|
|
switch (blitter->UsePaletteAnimation()) {
|
|
|
case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
|
|
|
UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
|
|
|
break;
|
|
|
|
|
|
case Blitter::PALETTE_ANIMATION_BLITTER:
|
|
|
blitter->PaletteAnimate(_local_palette);
|
|
|
break;
|
|
|
|
|
|
case Blitter::PALETTE_ANIMATION_NONE:
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
NOT_REACHED();
|
|
|
}
|
|
|
_cur_palette.count_dirty = 0;
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
|
_dirty_rect = {};
|
|
|
}
|
|
|
|
|
|
void VideoDriver_Win32GDI::PaintThread()
|
|
|
{
|
|
|
/* 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) {
|
|
|
this->Paint();
|
|
|
|
|
|
/* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
|
|
|
GdiFlush();
|
|
|
|
|
|
_draw_signal->wait(*_draw_mutex);
|
|
|
}
|
|
|
}
|