31#include <versionhelpers.h>
32#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
33#include <winrt/Windows.UI.ViewManagement.h>
38#include "../3rdparty/opengl/glext.h"
39#include "../3rdparty/opengl/wglext.h"
46#ifndef MAPVK_VK_TO_CHAR
47#define MAPVK_VK_TO_CHAR (2)
51#define PM_QS_INPUT 0x20000
55#define WM_DPICHANGED 0x02E0
66 MyShowCursor(
false,
true);
75#define AS(x, z) {x, 1, z}
76#define AM(x, y, z, w) {x, y - x + 1, z}
80 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
82 AM(
'A',
'Z',
'A',
'Z'),
83 AM(
'0',
'9',
'0',
'9'),
85 AS(VK_ESCAPE, WKC_ESC),
86 AS(VK_PAUSE, WKC_PAUSE),
87 AS(VK_BACK, WKC_BACKSPACE),
88 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
90 AS(VK_SPACE, WKC_SPACE),
91 AS(VK_RETURN, WKC_RETURN),
95 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
98 AM(VK_NUMPAD0, VK_NUMPAD9,
'0',
'9'),
99 AS(VK_DIVIDE, WKC_NUM_DIV),
100 AS(VK_MULTIPLY, WKC_NUM_MUL),
101 AS(VK_SUBTRACT, WKC_NUM_MINUS),
102 AS(VK_ADD, WKC_NUM_PLUS),
103 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
119static uint MapWindowsKey(uint sym)
123 for (
const auto &map : _vk_mapping) {
124 if (
IsInsideBS(sym, map.vk_from, map.vk_count)) {
125 key = sym - map.vk_from + map.map_to;
130 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
131 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
132 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
156 _fullscreen = full_screen;
176 if (
settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
181 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
183 GetWindowRect(GetDesktopWindow(), &r);
186 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
191 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
197 ChangeDisplaySettings(
nullptr, 0);
199 this->
width = _bck_resolution.width;
200 this->
height = _bck_resolution.height;
205 DWORD style, showstyle;
208 showstyle = SW_SHOWNORMAL;
214 style = WS_OVERLAPPEDWINDOW;
216 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
220 AdjustWindowRect(&r, style, FALSE);
221 w = r.right - r.left;
222 h = r.bottom - r.top;
225 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
233 mi.cbSize =
sizeof(mi);
234 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
236 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
237 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
241 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
242 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
243 ShowWindow(this->
main_wnd, showstyle);
261 static char32_t prev_char = 0;
265 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
266 prev_char = charcode;
271 if (prev_char != 0) {
275 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
291 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
300 HIMC hIMC = ImmGetContext(hwnd);
301 if (hIMC !=
nullptr) {
303 cf.dwStyle = CFS_POINT;
311 cf.ptCurrentPos.x = 0;
312 cf.ptCurrentPos.y = 0;
314 ImmSetCompositionWindow(hIMC, &cf);
316 ImmReleaseContext(hwnd, hIMC);
325 HIMC hIMC = ImmGetContext(hwnd);
326 if (hIMC !=
nullptr) {
329 cf.dwStyle = CFS_EXCLUDE;
343 cf.rcArea.right = cf.rcArea.left +
_focused_window->nested_focus->current_x;
344 cf.rcArea.bottom = cf.rcArea.top +
_focused_window->nested_focus->current_y;
347 cf.ptCurrentPos.x = 0;
348 cf.ptCurrentPos.y = 0;
349 SetRectEmpty(&cf.rcArea);
351 ImmSetCandidateWindow(hIMC, &cf);
353 ImmReleaseContext(hwnd, hIMC);
365 HIMC hIMC = ImmGetContext(hwnd);
367 if (hIMC !=
nullptr) {
368 if (lParam & GCS_RESULTSTR) {
370 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
371 std::wstring str(len + 1, L
'\0');
372 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
373 str[len /
sizeof(wchar_t)] = L
'\0';
383 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
388 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
389 std::wstring str(len + 1, L
'\0');
390 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
391 str[len /
sizeof(wchar_t)] = L
'\0';
394 static char utf8_buf[1024];
398 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
400 auto caret = view.begin();
401 const auto end = view.end();
402 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && caret != end && caret_bytes > 0; c++, caret_bytes--) {
416 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
419 ImmReleaseContext(hwnd, hIMC);
421 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
430 HIMC hIMC = ImmGetContext(hwnd);
431 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
432 ImmReleaseContext(hwnd, hIMC);
437#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
445extern "C" int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(int32_t error,
void *message,
void *languageException)
noexcept
447 typedef BOOL(WINAPI *PFNRoOriginateLanguageException)(int32_t,
void *,
void *);
448 static PFNRoOriginateLanguageException RoOriginateLanguageException = _combase.GetFunction(
"RoOriginateLanguageException");
450 if (RoOriginateLanguageException !=
nullptr) {
451 return RoOriginateLanguageException(error, message, languageException);
457extern "C" int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(
void *classId, winrt::guid
const &iid,
void **factory)
noexcept
459 typedef BOOL(WINAPI *PFNRoGetActivationFactory)(
void *, winrt::guid
const &,
void **);
460 static PFNRoGetActivationFactory RoGetActivationFactory = _combase.GetFunction(
"RoGetActivationFactory");
462 if (RoGetActivationFactory !=
nullptr) {
463 return RoGetActivationFactory(classId, iid, factory);
466 return winrt::impl::error_class_not_available;
471static bool IsDarkModeEnabled()
474#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
475 if (IsWindows10OrGreater()) {
485 winrt::Windows::UI::ViewManagement::UISettings
settings;
486 auto foreground =
settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
489 return ((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128);
500static void SetDarkModeForWindow(HWND hWnd,
bool dark_mode)
503#if defined(NTDDI_WIN10)
504 if (!IsWindows10OrGreater())
return;
509 typedef HRESULT(WINAPI *PFNDWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD);
510 static const PFNDWMSETWINDOWATTRIBUTE DwmSetWindowAttribute = _dwmapi.GetFunction(
"DwmSetWindowAttribute");
512 if (DwmSetWindowAttribute !=
nullptr) {
516 BOOL value = dark_mode ? TRUE : FALSE;
517 if (DwmSetWindowAttribute(hWnd, 20 , &value,
sizeof(value)) != S_OK) {
518 DwmSetWindowAttribute(hWnd, 19 , &value,
sizeof(value));
524LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
526 static uint32_t keycode = 0;
527 static bool console =
false;
529 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f / WHEEL_DELTA;
535 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
536 _cursor.in_window =
false;
541 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
544 case WM_SETTINGCHANGE:
546 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
551 GetUpdateRect(hwnd, &r, FALSE);
552 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
554 ValidateRect(hwnd,
nullptr);
558 case WM_PALETTECHANGED:
559 if ((HWND)wParam == hwnd)
return 0;
562 case WM_QUERYNEWPALETTE:
567 HandleExitGameRequest();
602 _cursor.in_window =
false;
608 int x = (int16_t)LOWORD(lParam);
609 int y = (int16_t)HIWORD(lParam);
614 if (!_cursor.in_window) {
615 _cursor.in_window =
true;
617 tme.cbSize =
sizeof(tme);
618 tme.dwFlags = TME_LEAVE;
619 tme.hwndTrack = hwnd;
621 TrackMouseEvent(&tme);
624 if (_cursor.fix_at) {
628 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
629 x = (int16_t)LOWORD(m.lParam);
630 y = (int16_t)HIWORD(m.lParam);
634 if (_cursor.UpdateCursorPosition(x, y)) {
636 pt.x = _cursor.pos.x;
637 pt.y = _cursor.pos.y;
638 ClientToScreen(hwnd, &pt);
639 SetCursorPos(pt.x, pt.y);
646 case WM_INPUTLANGCHANGE:
650 case WM_IME_SETCONTEXT:
655 case WM_IME_STARTCOMPOSITION:
660 case WM_IME_COMPOSITION:
663 case WM_IME_ENDCOMPOSITION:
674 console =
GB(lParam, 16, 8) == 41;
678 uint scancode =
GB(lParam, 16, 8);
679 uint charcode = wParam;
683 if (console && scancode == 41) {
690 uint cur_keycode = keycode;
698 uint scancode =
GB(lParam, 16, 8);
699 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
701 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
712 if (
HasBit(charcode, 31) && !console) {
713 if (scancode == 41) {
722 uint cur_keycode = keycode;
752 if (wParam != SIZE_MINIMIZED) {
755 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
756 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
762 RECT *r = (RECT*)lParam;
766 SetRect(&r2, 0, 0, 0, 0);
767 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
769 w = r->right - r->left - (r2.right - r2.left);
770 h = r->bottom - r->top - (r2.bottom - r2.top);
773 SetRect(&r2, 0, 0, w, h);
775 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
776 w = r2.right - r2.left;
777 h = r2.bottom - r2.top;
781 r->bottom = r->top + h;
784 case WMSZ_BOTTOMLEFT:
785 r->bottom = r->top + h;
786 r->left = r->right - w;
789 case WMSZ_BOTTOMRIGHT:
790 r->bottom = r->top + h;
791 r->right = r->left + w;
795 r->left = r->right - w;
799 r->right = r->left + w;
803 r->top = r->bottom - h;
807 r->top = r->bottom - h;
808 r->left = r->right - w;
812 r->top = r->bottom - h;
813 r->right = r->left + w;
819 case WM_DPICHANGED: {
823 RECT *prcNewWindow = (RECT *)lParam;
828 prcNewWindow->right - prcNewWindow->left,
829 prcNewWindow->bottom - prcNewWindow->top,
830 SWP_NOZORDER | SWP_NOACTIVATE);
838#if !defined(WM_MOUSEWHEEL)
839# define WM_MOUSEWHEEL 0x020A
841#if !defined(WM_MOUSEHWHEEL)
842# define WM_MOUSEHWHEEL 0x020E
844#if !defined(GET_WHEEL_DELTA_WPARAM)
845# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
848 case WM_MOUSEWHEEL: {
849 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
853 }
else if (delta > 0) {
857 _cursor.v_wheel -=
static_cast<float>(delta) * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
858 _cursor.wheel_moved =
true;
863 case WM_MOUSEHWHEEL: {
864 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
866 _cursor.h_wheel +=
static_cast<float>(delta) * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
867 _cursor.wheel_moved =
true;
883 if (_exit_game)
break;
885 bool active = (LOWORD(wParam) != WA_INACTIVE);
886 bool minimized = (HIWORD(wParam) != 0);
888 if (active && minimized) {
891 ShowWindow(hwnd, SW_RESTORE);
894 }
else if (!active && !minimized) {
896 ShowWindow(hwnd, SW_MINIMIZE);
897 ChangeDisplaySettings(
nullptr, 0);
904 return DefWindowProc(hwnd, msg, wParam, lParam);
907static void RegisterWndClass()
909 static bool registered =
false;
911 if (registered)
return;
913 HINSTANCE hinst = GetModuleHandle(
nullptr);
920 LoadIcon(hinst, MAKEINTRESOURCE(100)),
921 LoadCursor(
nullptr, IDC_ARROW),
928 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
931static const Dimension default_resolutions[] = {
950 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
951 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
953 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
958 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
964std::optional<std::string_view> VideoDriver_Win32Base::Initialize()
967 if (FAILED(CoInitializeEx(
nullptr, COINIT_MULTITHREADED)))
return "COM initialization failed";
986 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
998 this->
MakeDirty(0, 0, _screen.width, _screen.height);
1029 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
1033 DispatchMessage(&mesg);
1043 if (_exit_game)
break;
1072 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
1095static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
1097 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
1099 MONITORINFOEX monitorInfo = {};
1100 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
1101 GetMonitorInfo(hMonitor, &monitorInfo);
1103 DEVMODE devMode = {};
1104 devMode.dmSize =
sizeof(DEVMODE);
1105 devMode.dmDriverExtra = 0;
1106 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
1108 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
1114 std::vector<int> rates = {};
1115 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
1121 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
1130 assert(_screen.dst_ptr !=
nullptr);
1137 assert(_screen.dst_ptr !=
nullptr);
1138 if (_screen.dst_ptr !=
nullptr) {
1141 _screen.dst_ptr =
nullptr;
1154 auto err = this->Initialize();
1155 if (err)
return err;
1157 this->MakePalette();
1165 return std::nullopt;
1180 w = std::max(w, 64);
1181 h = std::max(h, 64);
1183 if (!force && w == _screen.width && h == _screen.height)
return false;
1185 BITMAPINFO *bi = (BITMAPINFO *)
new char[
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256]();
1186 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1188 bi->bmiHeader.biWidth = this->
width = w;
1189 bi->bmiHeader.biHeight = -(this->
height = h);
1191 bi->bmiHeader.biPlanes = 1;
1192 bi->bmiHeader.biBitCount = bpp;
1193 bi->bmiHeader.biCompression = BI_RGB;
1198 this->
dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&this->
buffer_bits,
nullptr, 0);
1201 UserError(
"CreateDIBSection failed");
1206 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1220void VideoDriver_Win32GDI::MakePalette()
1224 LOGPALETTE *pal = (LOGPALETTE *)
new char[
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY)]();
1226 pal->palVersion = 0x300;
1227 pal->palNumEntries = 256;
1229 for (uint i = 0; i != 256; i++) {
1233 pal->palPalEntry[i].peFlags = 0;
1238 if (this->
gdi_palette ==
nullptr) UserError(
"CreatePalette failed!\n");
1241void VideoDriver_Win32GDI::UpdatePalette(HDC dc, uint start, uint count)
1245 for (uint i = 0; i != count; i++) {
1249 rgb[i].rgbReserved = 0;
1252 SetDIBColorTable(dc, start, count, rgb);
1257 HDC hDC = GetWindowDC(hWnd);
1258 HPALETTE hOldPalette = SelectPalette(hDC, this->
gdi_palette, FALSE);
1259 UINT nChanged = RealizePalette(hDC);
1261 SelectPalette(hDC, hOldPalette, TRUE);
1262 ReleaseDC(hWnd, hDC);
1263 if (nChanged != 0) this->
MakeDirty(0, 0, _screen.width, _screen.height);
1273 HDC dc2 = CreateCompatibleDC(dc);
1275 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, this->
dib_sect);
1276 HPALETTE old_palette = SelectPalette(dc, this->
gdi_palette, FALSE);
1300 BitBlt(dc, 0, 0, this->
width, this->
height, dc2, 0, 0, SRCCOPY);
1301 SelectPalette(dc, old_palette, TRUE);
1302 SelectObject(dc2, old_bmp);
1313 int VideoDriver_Win32GDI::RedrawScreenDebug()
1331#ifndef PFD_SUPPORT_COMPOSITION
1332# define PFD_SUPPORT_COMPOSITION 0x00008000
1335static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1336static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1337static bool _hasWGLARBCreateContextProfile =
false;
1346 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1347 if (ret ==
nullptr) {
1349 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1359static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1361 PIXELFORMATDESCRIPTOR pfd = {
1362 sizeof(PIXELFORMATDESCRIPTOR),
1364 PFD_DRAW_TO_WINDOW |
1365 PFD_SUPPORT_OPENGL |
1370 0, 0, 0, 0, 0, 0, 0, 0,
1378 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1381 int format = ChoosePixelFormat(dc, &pfd);
1382 if (format == 0)
return "No suitable pixel format found";
1383 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1385 return std::nullopt;
1389static void LoadWGLExtensions()
1396 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1397 HDC dc = GetDC(wnd);
1400 if (SelectPixelFormat(dc) == std::nullopt) {
1402 HGLRC rc = wglCreateContext(dc);
1403 if (rc !=
nullptr) {
1404 wglMakeCurrent(dc, rc);
1408#pragma GCC diagnostic push
1409#pragma GCC diagnostic ignored "-Wcast-function-type"
1413 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1414 if (wglGetExtensionsStringARB !=
nullptr) {
1415 std::string_view wgl_exts = wglGetExtensionsStringARB(dc);
1418 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1422 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1427#pragma GCC diagnostic pop
1429 wglMakeCurrent(
nullptr,
nullptr);
1430 wglDeleteContext(rc);
1438static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1440std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1446 LoadWGLExtensions();
1448 auto err = this->Initialize();
1454 this->MakeWindow(_fullscreen);
1457 err = this->AllocateContext();
1464 this->driver_info = GetName();
1465 this->driver_info +=
" (";
1467 this->driver_info +=
")";
1469 this->ClientSizeChanged(this->width, this->height,
true);
1471 if (_screen.dst_ptr ==
nullptr) {
1474 return "Can't get pointer to screen buffer";
1477 this->ReleaseVideoPointer();
1483 return std::nullopt;
1486void VideoDriver_Win32OpenGL::Stop()
1488 this->DestroyContext();
1492void VideoDriver_Win32OpenGL::DestroyContext()
1496 wglMakeCurrent(
nullptr,
nullptr);
1497 if (this->gl_rc !=
nullptr) {
1498 wglDeleteContext(this->gl_rc);
1499 this->gl_rc =
nullptr;
1501 if (this->dc !=
nullptr) {
1502 ReleaseDC(this->main_wnd, this->dc);
1507void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1509 if (_wglSwapIntervalEXT !=
nullptr) {
1510 _wglSwapIntervalEXT(vsync);
1512 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1516std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1518 this->dc = GetDC(this->main_wnd);
1520 auto err = SelectPixelFormat(this->dc);
1521 if (err)
return err;
1526 if (_wglCreateContextAttribsARB !=
nullptr) {
1529 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1530 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1531 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1532 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1535 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1537 if (rc ==
nullptr) {
1541 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1545 if (rc ==
nullptr) {
1547 rc = wglCreateContext(this->dc);
1548 if (rc ==
nullptr)
return "Can't create OpenGL context";
1550 if (!wglMakeCurrent(this->dc, rc))
return "Can't activate GL context";
1558bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1560 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1561 this->DestroyContext();
1563 res &= this->AllocateContext() == std::nullopt;
1564 this->ClientSizeChanged(this->width, this->height,
true);
1568bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1571 this->ClientSizeChanged(this->width, this->height,
true);
1575void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1580void VideoDriver_Win32OpenGL::ClearSystemSprites()
1585bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1587 if (!force && w == _screen.width && h == _screen.height)
return false;
1589 this->width = w = std::max(w, 64);
1590 this->height = h = std::max(h, 64);
1592 if (this->gl_rc ==
nullptr)
return false;
1594 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1596 this->dirty_rect = {};
1598 SwapBuffers(this->dc);
1599 _screen.dst_ptr = this->GetVideoPointer();
1604void *VideoDriver_Win32OpenGL::GetVideoPointer()
1612void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1616 this->dirty_rect = {};
1617 _screen.dst_ptr =
nullptr;
1618 this->anim_buffer =
nullptr;
1621void VideoDriver_Win32OpenGL::Paint()
1640 SwapBuffers(this->dc);
#define AS(ap_name, size_x, size_y, min_year, max_year, catchment, noise, maint_cost, ttdpatch_type, class_id, name, preview)
AirportSpec definition for airports with at least one depot.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
How all blitters should look like.
virtual uint8_t GetScreenDepth()=0
Get the screen depth this blitter works for.
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
@ None
No palette animation.
@ Blitter
The blitter takes care of the palette animation.
@ VideoBackend
Palette animation should be done by video backend (8bpp only!).
virtual void PostResize()
Post resize event.
The factory for Windows' video driver.
void Paint()
Render video buffer to the screen.
uint8_t * GetAnimBuffer()
Get a pointer to the memory for the separate animation buffer.
void * GetVideoBuffer()
Get a pointer to the memory for the video driver to draw to.
bool Resize(int w, int h, bool force=false)
Change the size of the drawing window and allocate matching resources.
static std::optional< std::string_view > Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res)
Create and initialize the singleton back-end class.
void UpdatePalette(const Colour *pal, uint first, uint length)
Update the stored palette.
void ReleaseAnimBuffer(const Rect &update_rect)
Update animation buffer texture after the animation buffer was filled.
void ClearCursorCache()
Queue a request for cursor cache clear.
static OpenGLBackend * Get()
Get singleton instance of this class.
void DrawMouseCursor()
Draw mouse cursor on screen.
void ReleaseVideoBuffer(const Rect &update_rect)
Update video buffer texture after the video buffer was filled.
static void Destroy()
Free resources and destroy singleton back-end class.
Constant span of UTF-8 encoded data.
Base class for Windows video drivers.
int height
Height in pixels of our display surface.
bool has_focus
Does our window have system focus?
void EditBoxLostFocus() override
An edit box lost the input focus.
void CheckPaletteAnim() override
Process any pending palette animation.
void Stop() override
Stop this driver.
int height_org
Original monitor resolution height, before we changed it.
HWND main_wnd
Handle to system window.
bool fullscreen
Whether to use (true) fullscreen mode.
int width_org
Original monitor resolution width, before we changed it.
bool MakeWindow(bool full_screen, bool resize=true)
Instantiate a new window.
bool buffer_locked
Video buffer was locked by the main thread.
virtual void * GetVideoPointer()=0
Get a pointer to the video buffer.
void MakeDirty(int left, int top, int width, int height) override
Mark a particular area dirty.
bool PollEvent() override
Process a single system event.
virtual void PaletteChanged(HWND hWnd)=0
Palette of the window has changed.
bool LockVideoBuffer() override
Make sure the video buffer is ready for drawing.
std::vector< int > GetListOfMonitorRefreshRates() override
Get a list of refresh rates of each available monitor.
virtual bool AllocateBackingStore(int w, int h, bool force=false)=0
(Re-)create the backing store.
int width
Width in pixels of our display surface.
void InputLoop() override
Handle input logic, is CTRL pressed, should we fast-forward, etc.
virtual uint8_t GetFullscreenBpp()
Colour depth to use for fullscreen display modes.
Dimension GetScreenSize() const override
Get the resolution of the main screen.
virtual void ReleaseVideoPointer()
Hand video buffer back to the painting backend.
void UnlockVideoBuffer() override
Unlock a previously locked video buffer.
void ClientSizeChanged(int w, int h, bool force=false)
Indicate to the driver the client-size might have changed.
void MainLoop() override
Perform the actual drawing.
void ClaimMousePointer() override
Claim the exclusive rights for the mouse pointer.
Rect dirty_rect
Region of the screen that needs redrawing.
bool ToggleFullscreen(bool fullscreen) override
Change the full screen setting.
bool ChangeResolution(int w, int h) override
Change the resolution of the window.
The GDI video driver for windows.
void * buffer_bits
Internal rendering buffer.
HBITMAP dib_sect
System bitmap object referencing our rendering buffer.
void PaletteChanged(HWND hWnd) override
Palette of the window has changed.
std::optional< std::string_view > Start(const StringList ¶m) override
Start this driver.
void * GetVideoPointer() override
Get a pointer to the video buffer.
HPALETTE gdi_palette
Palette object for 8bpp blitter.
bool AllocateBackingStore(int w, int h, bool force=false) override
(Re-)create the backing store.
void Paint() override
Paint the window.
bool AfterBlitterChange() override
Callback invoked after the blitter was changed.
void Stop() override
Stop this driver.
bool fast_forward_key_pressed
The fast-forward key is being pressed.
void Tick()
Give the video-driver a tick.
void SleepTillNextTick()
Sleep till the next tick is about to happen.
void StartGameThread()
Start the loop for game-tick.
static std::string GetCaption()
Get the caption to use for the game's title bar.
void StopGameThread()
Stop the loop for the game-tick.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
void UpdateAutoResolution()
Apply resolution auto-detection and clamp to sensible defaults.
static Palette _local_palette
Current palette to use for drawing.
static OGLProc GetOGLProcAddressCallback(const char *proc)
Platform-specific callback to get an OpenGL function pointer.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
bool GetDriverParamBool(const StringList &parm, std::string_view name)
Get a boolean parameter the list of parameters.
std::vector< Dimension > _resolutions
List of resolutions.
Dimension _cur_resolution
The current resolution.
Error reporting related functions.
Factory to 'query' all available blitters.
fluid_settings_t * settings
FluidSynth settings handle.
Types for recording game performance data.
@ Video
Speed of painting drawn video buffer.
Rect BoundingRect(const Rect &r1, const Rect &r2)
Compute the bounding rectangle around two rectangles.
bool IsEmptyRect(const Rect &r)
Check if a rectangle is empty.
bool _shift_pressed
Is Shift pressed?
bool _left_button_down
Is left mouse button pressed?
bool _ctrl_pressed
Is Ctrl pressed?
bool _left_button_clicked
Is left mouse button clicked?
bool _right_button_clicked
Is right mouse button clicked?
Support8bpp _support8bpp
State of the support for 8bpp graphics.
DirectionKeys _dirkeys
Pressed direction keys.
bool _right_button_down
Is right mouse button pressed?
bool AdjustGUIZoom(bool automatic)
Resolve GUI zoom level and adjust GUI to new zoom, if auto-suggestion is requested.
Functions related to the gfx engine.
void HandleCtrlChanged()
State of CONTROL key has changed.
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
void GameSizeChanged()
Size of the application screen changed.
void HandleMouseEvents()
Handle a mouse event from the video driver.
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
void HandleTextInput(std::string_view str, bool marked=false, std::optional< size_t > caret=std::nullopt, std::optional< size_t > insert_location=std::nullopt, std::optional< size_t > replacement_end=std::nullopt)
Handle text input.
@ Hardware
Full 8bpp support by OS and hardware.
@ WKC_BACKSLASH
\ Backslash
@ WKC_SLASH
/ Forward slash
@ WKC_SINGLEQUOTE
' Single quote
@ WKC_R_BRACKET
] Right square bracket
@ WKC_L_BRACKET
[ Left square bracket
@ WKC_SEMICOLON
; Semicolon
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Functions/types related to loading libraries dynamically.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
bool HasStringInExtensionList(std::string_view string, std::string_view substring)
Find a substring in a string made of space delimited elements.
OpenGL video driver support.
void GetKeyboardLayout()
Retrieve keyboard layout from language string or (if set) config file.
bool CopyPalette(Palette &local_palette, bool force_copy)
Copy the current palette if the palette was updated.
Functions related to modal progress.
Pseudo random number generator.
A number of safeguards to prevent using unsafe methods.
static void FindResolutions()
Find and store all possible resolutions into _resolutions.
ClientSettings _settings_client
The current settings for this game.
Definition of base types and functions in a cross-platform compatible way.
char32_t Utf16DecodeSurrogate(uint lead, uint trail)
Convert an UTF-16 surrogate pair to the corresponding Unicode character.
bool Utf16IsLeadSurrogate(uint c)
Is the given character a lead surrogate code point?
bool Utf16IsTrailSurrogate(uint c)
Is the given character a lead surrogate code point?
std::vector< std::string > StringList
Type for a list of strings.
Dimensions (a width and height) of a rectangle in 2D.
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Specification of a rectangle with absolute coordinates of all edges.
Functions related to text effects.
Handling of UTF-8 encoded data.
bool _video_vsync
Whether we should use vsync (only if active video driver supports HW acceleration).
std::string_view convert_from_fs(const std::wstring_view src, std::span< char > dst_buf)
Convert to OpenTTD's encoding from that of the environment in UNICODE.
std::wstring OTTD2FS(std::string_view name)
Convert from OpenTTD's encoding to a wide string.
std::string FS2OTTD(std::wstring_view name)
Convert to OpenTTD's encoding from a wide string.
Declarations of functions for MS windows systems.
static LRESULT HandleCharMsg(uint keycode, char32_t charcode)
Forward key presses to the window system.
static bool DrawIMECompositionString()
Should we draw the composition string ourself, i.e is this a normal IME?
static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam)
Handle WM_IME_COMPOSITION messages.
static void SetCandidatePos(HWND hwnd)
Set the position of the candidate window.
static void CancelIMEComposition(HWND hwnd)
Clear the current composition string.
static void SetCompositionPos(HWND hwnd)
Set position of the composition window to the caret position.
Base of the Windows video driver.
Window * _focused_window
Window that currently has focus.
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.