11#include "../openttd.h"
12#include "../error_func.h"
13#include "../gfx_func.h"
14#include "../os/windows/win32.h"
15#include "../blitter/factory.hpp"
16#include "../core/geometry_func.hpp"
17#include "../core/math_func.hpp"
18#include "../core/random_func.hpp"
19#include "../texteff.hpp"
21#include "../progress.h"
22#include "../window_gui.h"
23#include "../window_func.h"
24#include "../framerate_type.h"
25#include "../library_loader.h"
26#include "../core/utf8.hpp"
30#include <versionhelpers.h>
31#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
32#include <winrt/Windows.UI.ViewManagement.h>
35#include "../safeguards.h"
38#ifndef MAPVK_VK_TO_CHAR
39#define MAPVK_VK_TO_CHAR (2)
43#define PM_QS_INPUT 0x20000
47#define WM_DPICHANGED 0x02E0
56bool VideoDriver_Win32Base::ClaimMousePointer()
58 MyShowCursor(
false,
true);
68#define AS(x, z) {x, 1, z}
69#define AM(x, y, z, w) {x, y - x + 1, z}
73 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
75 AM(
'A',
'Z',
'A',
'Z'),
76 AM(
'0',
'9',
'0',
'9'),
78 AS(VK_ESCAPE, WKC_ESC),
79 AS(VK_PAUSE, WKC_PAUSE),
80 AS(VK_BACK, WKC_BACKSPACE),
81 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
83 AS(VK_SPACE, WKC_SPACE),
84 AS(VK_RETURN, WKC_RETURN),
88 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
91 AM(VK_NUMPAD0, VK_NUMPAD9,
'0',
'9'),
92 AS(VK_DIVIDE, WKC_NUM_DIV),
93 AS(VK_MULTIPLY, WKC_NUM_MUL),
94 AS(VK_SUBTRACT, WKC_NUM_MINUS),
95 AS(VK_ADD, WKC_NUM_PLUS),
96 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
112static uint MapWindowsKey(uint sym)
116 for (
const auto &map : _vk_mapping) {
117 if (
IsInsideBS(sym, map.vk_from, map.vk_count)) {
118 key = sym - map.vk_from + map.map_to;
123 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
124 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
125 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
146 _fullscreen = full_screen;
166 if (
settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
171 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
173 GetWindowRect(GetDesktopWindow(), &r);
176 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
181 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
187 ChangeDisplaySettings(
nullptr, 0);
189 this->
width = _bck_resolution.width;
190 this->
height = _bck_resolution.height;
195 DWORD style, showstyle;
198 showstyle = SW_SHOWNORMAL;
204 style = WS_OVERLAPPEDWINDOW;
206 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
210 AdjustWindowRect(&r, style, FALSE);
211 w = r.right - r.left;
212 h = r.bottom - r.top;
215 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
223 mi.cbSize =
sizeof(mi);
224 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
226 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
227 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
231 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
232 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
233 ShowWindow(this->
main_wnd, showstyle);
246 static char32_t prev_char = 0;
250 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
251 prev_char = charcode;
256 if (prev_char != 0) {
260 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
273 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
279 HIMC hIMC = ImmGetContext(hwnd);
280 if (hIMC !=
nullptr) {
282 cf.dwStyle = CFS_POINT;
287 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
288 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
290 cf.ptCurrentPos.x = 0;
291 cf.ptCurrentPos.y = 0;
293 ImmSetCompositionWindow(hIMC, &cf);
295 ImmReleaseContext(hwnd, hIMC);
301 HIMC hIMC = ImmGetContext(hwnd);
302 if (hIMC !=
nullptr) {
305 cf.dwStyle = CFS_EXCLUDE;
309 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
310 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
312 cf.rcArea.left = _focused_window->
left;
313 cf.rcArea.top = _focused_window->
top;
314 cf.rcArea.right = _focused_window->
left + _focused_window->
width;
315 cf.rcArea.bottom = _focused_window->
top + _focused_window->
height;
323 cf.ptCurrentPos.x = 0;
324 cf.ptCurrentPos.y = 0;
325 SetRectEmpty(&cf.rcArea);
327 ImmSetCandidateWindow(hIMC, &cf);
329 ImmReleaseContext(hwnd, hIMC);
335 HIMC hIMC = ImmGetContext(hwnd);
337 if (hIMC !=
nullptr) {
338 if (lParam & GCS_RESULTSTR) {
340 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
341 std::wstring str(len + 1, L
'\0');
342 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
343 str[len /
sizeof(wchar_t)] = L
'\0';
353 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
358 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
359 std::wstring str(len + 1, L
'\0');
360 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
361 str[len /
sizeof(wchar_t)] = L
'\0';
364 static char utf8_buf[1024];
368 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
370 auto caret = view.begin();
371 const auto end = view.end();
372 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && caret != end && caret_bytes > 0; c++, caret_bytes--) {
386 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
389 ImmReleaseContext(hwnd, hIMC);
391 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
397 HIMC hIMC = ImmGetContext(hwnd);
398 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
399 ImmReleaseContext(hwnd, hIMC);
404#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
412extern "C" int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(int32_t error,
void *message,
void *languageException)
noexcept
414 typedef BOOL(WINAPI *PFNRoOriginateLanguageException)(int32_t,
void *,
void *);
415 static PFNRoOriginateLanguageException RoOriginateLanguageException = _combase.GetFunction(
"RoOriginateLanguageException");
417 if (RoOriginateLanguageException !=
nullptr) {
418 return RoOriginateLanguageException(error, message, languageException);
424extern "C" int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(
void *classId, winrt::guid
const &iid,
void **factory)
noexcept
426 typedef BOOL(WINAPI *PFNRoGetActivationFactory)(
void *, winrt::guid
const &,
void **);
427 static PFNRoGetActivationFactory RoGetActivationFactory = _combase.GetFunction(
"RoGetActivationFactory");
429 if (RoGetActivationFactory !=
nullptr) {
430 return RoGetActivationFactory(classId, iid, factory);
433 return winrt::impl::error_class_not_available;
438static bool IsDarkModeEnabled()
441#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
442 if (IsWindows10OrGreater()) {
452 winrt::Windows::UI::ViewManagement::UISettings
settings;
453 auto foreground =
settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
456 return ((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128);
467static void SetDarkModeForWindow(HWND hWnd,
bool dark_mode)
470#if defined(NTDDI_WIN10)
471 if (!IsWindows10OrGreater())
return;
476 typedef HRESULT(WINAPI *PFNDWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD);
477 static const PFNDWMSETWINDOWATTRIBUTE DwmSetWindowAttribute = _dwmapi.GetFunction(
"DwmSetWindowAttribute");
479 if (DwmSetWindowAttribute !=
nullptr) {
483 BOOL value = dark_mode ? TRUE : FALSE;
484 if (DwmSetWindowAttribute(hWnd, 20 , &value,
sizeof(value)) != S_OK) {
485 DwmSetWindowAttribute(hWnd, 19 , &value,
sizeof(value));
491LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
493 static uint32_t keycode = 0;
494 static bool console =
false;
496 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f / WHEEL_DELTA;
502 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
508 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
511 case WM_SETTINGCHANGE:
513 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
518 GetUpdateRect(hwnd, &r, FALSE);
519 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
521 ValidateRect(hwnd,
nullptr);
525 case WM_PALETTECHANGED:
526 if ((HWND)wParam == hwnd)
return 0;
529 case WM_QUERYNEWPALETTE:
534 HandleExitGameRequest();
575 int x = (int16_t)LOWORD(lParam);
576 int y = (int16_t)HIWORD(lParam);
584 tme.cbSize =
sizeof(tme);
585 tme.dwFlags = TME_LEAVE;
586 tme.hwndTrack = hwnd;
588 TrackMouseEvent(&tme);
595 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
596 x = (int16_t)LOWORD(m.lParam);
597 y = (int16_t)HIWORD(m.lParam);
603 pt.x = _cursor.
pos.x;
604 pt.y = _cursor.
pos.y;
605 ClientToScreen(hwnd, &pt);
606 SetCursorPos(pt.x, pt.y);
613 case WM_INPUTLANGCHANGE:
617 case WM_IME_SETCONTEXT:
622 case WM_IME_STARTCOMPOSITION:
627 case WM_IME_COMPOSITION:
630 case WM_IME_ENDCOMPOSITION:
641 console =
GB(lParam, 16, 8) == 41;
645 uint scancode =
GB(lParam, 16, 8);
646 uint charcode = wParam;
650 if (console && scancode == 41) {
657 uint cur_keycode = keycode;
665 uint scancode =
GB(lParam, 16, 8);
666 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
668 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
679 if (
HasBit(charcode, 31) && !console) {
680 if (scancode == 41) {
689 uint cur_keycode = keycode;
719 if (wParam != SIZE_MINIMIZED) {
722 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
723 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
724 video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
729 RECT *r = (RECT*)lParam;
733 SetRect(&r2, 0, 0, 0, 0);
734 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
736 w = r->right - r->left - (r2.right - r2.left);
737 h = r->bottom - r->top - (r2.bottom - r2.top);
740 SetRect(&r2, 0, 0, w, h);
742 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
743 w = r2.right - r2.left;
744 h = r2.bottom - r2.top;
748 r->bottom = r->top + h;
751 case WMSZ_BOTTOMLEFT:
752 r->bottom = r->top + h;
753 r->left = r->right - w;
756 case WMSZ_BOTTOMRIGHT:
757 r->bottom = r->top + h;
758 r->right = r->left + w;
762 r->left = r->right - w;
766 r->right = r->left + w;
770 r->top = r->bottom - h;
774 r->top = r->bottom - h;
775 r->left = r->right - w;
779 r->top = r->bottom - h;
780 r->right = r->left + w;
786 case WM_DPICHANGED: {
790 RECT *prcNewWindow = (RECT *)lParam;
795 prcNewWindow->right - prcNewWindow->left,
796 prcNewWindow->bottom - prcNewWindow->top,
797 SWP_NOZORDER | SWP_NOACTIVATE);
805#if !defined(WM_MOUSEWHEEL)
806# define WM_MOUSEWHEEL 0x020A
808#if !defined(WM_MOUSEHWHEEL)
809# define WM_MOUSEHWHEEL 0x020E
811#if !defined(GET_WHEEL_DELTA_WPARAM)
812# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
815 case WM_MOUSEWHEEL: {
816 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
820 }
else if (delta > 0) {
825 _cursor.wheel_moved =
true;
830 case WM_MOUSEHWHEEL: {
831 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
834 _cursor.wheel_moved =
true;
850 if (_exit_game)
break;
852 bool active = (LOWORD(wParam) != WA_INACTIVE);
853 bool minimized = (HIWORD(wParam) != 0);
855 if (active && minimized) {
858 ShowWindow(hwnd, SW_RESTORE);
861 }
else if (!active && !minimized) {
863 ShowWindow(hwnd, SW_MINIMIZE);
864 ChangeDisplaySettings(
nullptr, 0);
871 return DefWindowProc(hwnd, msg, wParam, lParam);
874static void RegisterWndClass()
876 static bool registered =
false;
878 if (registered)
return;
880 HINSTANCE hinst = GetModuleHandle(
nullptr);
887 LoadIcon(hinst, MAKEINTRESOURCE(100)),
888 LoadCursor(
nullptr, IDC_ARROW),
895 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
898static const Dimension default_resolutions[] = {
912static void FindResolutions(uint8_t bpp)
917 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
918 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
920 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
925 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
931void VideoDriver_Win32Base::Initialize()
949 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
961 this->
MakeDirty(0, 0, _screen.width, _screen.height);
978 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
979 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
980 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
981 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
993 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
997 DispatchMessage(&mesg);
1007 if (_exit_game)
break;
1016void VideoDriver_Win32Base::ClientSizeChanged(
int w,
int h,
bool force)
1030 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
1053static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
1055 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
1057 MONITORINFOEX monitorInfo = {};
1058 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
1059 GetMonitorInfo(hMonitor, &monitorInfo);
1061 DEVMODE devMode = {};
1062 devMode.dmSize =
sizeof(DEVMODE);
1063 devMode.dmDriverExtra = 0;
1064 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
1066 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
1072 std::vector<int> rates = {};
1073 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
1079 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
1084 typedef UINT (WINAPI *PFNGETDPIFORWINDOW)(HWND hwnd);
1085 typedef UINT (WINAPI *PFNGETDPIFORSYSTEM)(VOID);
1086 typedef HRESULT (WINAPI *PFNGETDPIFORMONITOR)(HMONITOR hMonitor,
int dpiType, UINT *dpiX, UINT *dpiY);
1088 static PFNGETDPIFORWINDOW _GetDpiForWindow =
nullptr;
1089 static PFNGETDPIFORSYSTEM _GetDpiForSystem =
nullptr;
1090 static PFNGETDPIFORMONITOR _GetDpiForMonitor =
nullptr;
1092 static bool init_done =
false;
1097 _GetDpiForWindow = _user32.
GetFunction(
"GetDpiForWindow");
1098 _GetDpiForSystem = _user32.
GetFunction(
"GetDpiForSystem");
1099 _GetDpiForMonitor = _shcore.
GetFunction(
"GetDpiForMonitor");
1104 if (cur_dpi == 0 && _GetDpiForWindow !=
nullptr && this->
main_wnd !=
nullptr) {
1106 cur_dpi = _GetDpiForWindow(this->
main_wnd);
1108 if (cur_dpi == 0 && _GetDpiForMonitor !=
nullptr && this->
main_wnd !=
nullptr) {
1111 if (SUCCEEDED(_GetDpiForMonitor(MonitorFromWindow(this->
main_wnd, MONITOR_DEFAULTTOPRIMARY), 0 , &dpiX, &dpiY))) {
1115 if (cur_dpi == 0 && _GetDpiForSystem !=
nullptr) {
1117 cur_dpi = _GetDpiForSystem();
1120 return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f;
1129 assert(_screen.dst_ptr !=
nullptr);
1136 assert(_screen.dst_ptr !=
nullptr);
1137 if (_screen.dst_ptr !=
nullptr) {
1140 _screen.dst_ptr =
nullptr;
1155 this->MakePalette();
1163 return std::nullopt;
1178 w = std::max(w, 64);
1179 h = std::max(h, 64);
1181 if (!force && w == _screen.width && h == _screen.height)
return false;
1183 BITMAPINFO *bi = (BITMAPINFO *)
new char[
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256]();
1184 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1186 bi->bmiHeader.biWidth = this->
width = w;
1187 bi->bmiHeader.biHeight = -(this->
height = h);
1189 bi->bmiHeader.biPlanes = 1;
1190 bi->bmiHeader.biBitCount = bpp;
1191 bi->bmiHeader.biCompression = BI_RGB;
1196 this->
dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&this->
buffer_bits,
nullptr, 0);
1199 UserError(
"CreateDIBSection failed");
1204 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1218void VideoDriver_Win32GDI::MakePalette()
1222 LOGPALETTE *pal = (LOGPALETTE *)
new char[
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY)]();
1224 pal->palVersion = 0x300;
1225 pal->palNumEntries = 256;
1227 for (uint i = 0; i != 256; i++) {
1231 pal->palPalEntry[i].peFlags = 0;
1236 if (this->
gdi_palette ==
nullptr) UserError(
"CreatePalette failed!\n");
1239void VideoDriver_Win32GDI::UpdatePalette(HDC dc, uint start, uint count)
1243 for (uint i = 0; i != count; i++) {
1247 rgb[i].rgbReserved = 0;
1250 SetDIBColorTable(dc, start, count, rgb);
1255 HDC hDC = GetWindowDC(hWnd);
1256 HPALETTE hOldPalette = SelectPalette(hDC, this->
gdi_palette, FALSE);
1257 UINT nChanged = RealizePalette(hDC);
1259 SelectPalette(hDC, hOldPalette, TRUE);
1260 ReleaseDC(hWnd, hDC);
1261 if (nChanged != 0) this->
MakeDirty(0, 0, _screen.width, _screen.height);
1271 HDC dc2 = CreateCompatibleDC(dc);
1273 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, this->
dib_sect);
1274 HPALETTE old_palette = SelectPalette(dc, this->
gdi_palette, FALSE);
1298 BitBlt(dc, 0, 0, this->
width, this->
height, dc2, 0, 0, SRCCOPY);
1299 SelectPalette(dc, old_palette, TRUE);
1300 SelectObject(dc2, old_bmp);
1311 int VideoDriver_Win32GDI::RedrawScreenDebug()
1330#include "../3rdparty/opengl/glext.h"
1331#include "../3rdparty/opengl/wglext.h"
1334#ifndef PFD_SUPPORT_COMPOSITION
1335# define PFD_SUPPORT_COMPOSITION 0x00008000
1338static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1339static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1340static bool _hasWGLARBCreateContextProfile =
false;
1345 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1346 if (ret ==
nullptr) {
1348 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1358static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1360 PIXELFORMATDESCRIPTOR pfd = {
1361 sizeof(PIXELFORMATDESCRIPTOR),
1363 PFD_DRAW_TO_WINDOW |
1364 PFD_SUPPORT_OPENGL |
1369 0, 0, 0, 0, 0, 0, 0, 0,
1377 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1380 int format = ChoosePixelFormat(dc, &pfd);
1381 if (format == 0)
return "No suitable pixel format found";
1382 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1384 return std::nullopt;
1388static void LoadWGLExtensions()
1395 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1396 HDC dc = GetDC(wnd);
1399 if (SelectPixelFormat(dc) == std::nullopt) {
1401 HGLRC rc = wglCreateContext(dc);
1402 if (rc !=
nullptr) {
1403 wglMakeCurrent(dc, rc);
1407#pragma GCC diagnostic push
1408#pragma GCC diagnostic ignored "-Wcast-function-type"
1412 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1413 if (wglGetExtensionsStringARB !=
nullptr) {
1414 std::string_view wgl_exts = wglGetExtensionsStringARB(dc);
1416 if (HasStringInExtensionList(wgl_exts,
"WGL_ARB_create_context")) {
1417 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1419 _hasWGLARBCreateContextProfile = HasStringInExtensionList(wgl_exts,
"WGL_ARB_create_context_profile");
1420 if (HasStringInExtensionList(wgl_exts,
"WGL_EXT_swap_control")) {
1421 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1426#pragma GCC diagnostic pop
1428 wglMakeCurrent(
nullptr,
nullptr);
1429 wglDeleteContext(rc);
1437static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1439std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1445 LoadWGLExtensions();
1448 this->MakeWindow(_fullscreen);
1451 auto err = this->AllocateContext();
1458 this->driver_info = GetName();
1459 this->driver_info +=
" (";
1461 this->driver_info +=
")";
1463 this->ClientSizeChanged(this->width, this->height,
true);
1465 if (_screen.dst_ptr ==
nullptr) {
1468 return "Can't get pointer to screen buffer";
1471 this->ReleaseVideoPointer();
1477 return std::nullopt;
1480void VideoDriver_Win32OpenGL::Stop()
1482 this->DestroyContext();
1486void VideoDriver_Win32OpenGL::DestroyContext()
1490 wglMakeCurrent(
nullptr,
nullptr);
1491 if (this->gl_rc !=
nullptr) {
1492 wglDeleteContext(this->gl_rc);
1493 this->gl_rc =
nullptr;
1495 if (this->dc !=
nullptr) {
1496 ReleaseDC(this->main_wnd, this->dc);
1501void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1503 if (_wglSwapIntervalEXT !=
nullptr) {
1504 _wglSwapIntervalEXT(vsync);
1506 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1510std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1512 this->dc = GetDC(this->main_wnd);
1514 auto err = SelectPixelFormat(this->dc);
1515 if (err)
return err;
1520 if (_wglCreateContextAttribsARB !=
nullptr) {
1523 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1524 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1525 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1526 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1529 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1531 if (rc ==
nullptr) {
1535 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1539 if (rc ==
nullptr) {
1541 rc = wglCreateContext(this->dc);
1542 if (rc ==
nullptr)
return "Can't create OpenGL context";
1544 if (!wglMakeCurrent(this->dc, rc))
return "Can't active GL context";
1552bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1554 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1555 this->DestroyContext();
1557 res &= this->AllocateContext() == std::nullopt;
1558 this->ClientSizeChanged(this->width, this->height,
true);
1562bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1565 this->ClientSizeChanged(this->width, this->height,
true);
1569void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1574void VideoDriver_Win32OpenGL::ClearSystemSprites()
1579bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1581 if (!force && w == _screen.width && h == _screen.height)
return false;
1583 this->width = w = std::max(w, 64);
1584 this->height = h = std::max(h, 64);
1586 if (this->gl_rc ==
nullptr)
return false;
1588 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1590 this->dirty_rect = {};
1592 SwapBuffers(this->dc);
1593 _screen.dst_ptr = this->GetVideoPointer();
1598void *VideoDriver_Win32OpenGL::GetVideoPointer()
1606void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1610 this->dirty_rect = {};
1611 _screen.dst_ptr =
nullptr;
1612 this->anim_buffer =
nullptr;
1615void VideoDriver_Win32OpenGL::Paint()
1634 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.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
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.
Function GetFunction(const std::string &symbol_name)
Get a function from a loaded library.
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()
Get screen depth to use for fullscreen mode.
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 MainLoop() override
Perform the actual drawing.
Rect dirty_rect
Region of the screen that needs redrawing.
float GetDPIScale() override
Get DPI scaling factor of the screen OTTD is displayed on.
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.
#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.
fluid_settings_t * settings
FluidSynth settings handle.
@ PFE_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?
uint8_t _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
bool _left_button_clicked
Is left mouse button clicked?
bool _right_button_clicked
Is right mouse button clicked?
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.
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.
@ S8BPP_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.
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.
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.
static OGLProc GetOGLProcAddressCallback(const char *proc)
Platform-specific callback to get an OpenGL function pointer.
ClientSettings _settings_client
The current settings for this game.
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.
GUISettings gui
settings related to the GUI
bool UpdateCursorPosition(int x, int y)
Update cursor position on mouse movement.
bool fix_at
mouse is moving, but cursor is not (used for scrolling)
Point pos
logical mouse position
bool in_window
mouse inside this window, determines drawing logic
int wheel
mouse wheel movement
Dimensions (a width and height) of a rectangle in 2D.
uint8_t scrollwheel_multiplier
how much 'wheel' per incoming event from the OS?
Information about the currently used palette.
int first_dirty
The first dirty element.
int count_dirty
The number of dirty elements.
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Coordinates of a point in 2D.
Specification of a rectangle with absolute coordinates of all edges.
WindowClass window_class
Window class.
virtual Point GetCaretPosition() const
Get the current caret position if an edit box has the focus.
int left
x position of left edge of the window
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
int top
y position of top edge of the window
int height
Height of the window (number of pixels down in y direction)
int width
width of the window (number of pixels to the right in x direction)
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.
static Palette _local_palette
Current palette to use for drawing.
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.
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...
@ WC_CONSOLE
Console; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers: