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>
37#include "../3rdparty/opengl/glext.h"
38#include "../3rdparty/opengl/wglext.h"
42#include "../safeguards.h"
45#ifndef MAPVK_VK_TO_CHAR
46#define MAPVK_VK_TO_CHAR (2)
50#define PM_QS_INPUT 0x20000
54#define WM_DPICHANGED 0x02E0
63bool VideoDriver_Win32Base::ClaimMousePointer()
65 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;
153 _fullscreen = full_screen;
173 if (
settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
178 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
180 GetWindowRect(GetDesktopWindow(), &r);
183 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
188 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
194 ChangeDisplaySettings(
nullptr, 0);
196 this->
width = _bck_resolution.width;
197 this->
height = _bck_resolution.height;
202 DWORD style, showstyle;
205 showstyle = SW_SHOWNORMAL;
211 style = WS_OVERLAPPEDWINDOW;
213 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
217 AdjustWindowRect(&r, style, FALSE);
218 w = r.right - r.left;
219 h = r.bottom - r.top;
222 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
230 mi.cbSize =
sizeof(mi);
231 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
233 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
234 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
238 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
239 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
240 ShowWindow(this->
main_wnd, showstyle);
253 static char32_t prev_char = 0;
257 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
258 prev_char = charcode;
263 if (prev_char != 0) {
267 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
280 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
286 HIMC hIMC = ImmGetContext(hwnd);
287 if (hIMC !=
nullptr) {
289 cf.dwStyle = CFS_POINT;
294 cf.ptCurrentPos.
x = _focused_window->
left + pt.
x;
295 cf.ptCurrentPos.y = _focused_window->
top + pt.
y;
297 cf.ptCurrentPos.x = 0;
298 cf.ptCurrentPos.y = 0;
300 ImmSetCompositionWindow(hIMC, &cf);
302 ImmReleaseContext(hwnd, hIMC);
308 HIMC hIMC = ImmGetContext(hwnd);
309 if (hIMC !=
nullptr) {
312 cf.dwStyle = CFS_EXCLUDE;
316 cf.ptCurrentPos.
x = _focused_window->
left + pt.
x;
317 cf.ptCurrentPos.y = _focused_window->
top + pt.
y;
319 cf.rcArea.left = _focused_window->
left;
320 cf.rcArea.top = _focused_window->
top;
321 cf.rcArea.right = _focused_window->
left + _focused_window->
width;
322 cf.rcArea.bottom = _focused_window->
top + _focused_window->
height;
330 cf.ptCurrentPos.x = 0;
331 cf.ptCurrentPos.y = 0;
332 SetRectEmpty(&cf.rcArea);
334 ImmSetCandidateWindow(hIMC, &cf);
336 ImmReleaseContext(hwnd, hIMC);
342 HIMC hIMC = ImmGetContext(hwnd);
344 if (hIMC !=
nullptr) {
345 if (lParam & GCS_RESULTSTR) {
347 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
348 std::wstring str(len + 1, L
'\0');
349 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
350 str[len /
sizeof(wchar_t)] = L
'\0';
360 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
365 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
366 std::wstring str(len + 1, L
'\0');
367 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
368 str[len /
sizeof(wchar_t)] = L
'\0';
371 static char utf8_buf[1024];
375 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
377 auto caret = view.begin();
378 const auto end = view.end();
379 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && caret != end && caret_bytes > 0; c++, caret_bytes--) {
393 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
396 ImmReleaseContext(hwnd, hIMC);
398 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
404 HIMC hIMC = ImmGetContext(hwnd);
405 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
406 ImmReleaseContext(hwnd, hIMC);
411#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
419extern "C" int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(int32_t error,
void *message,
void *languageException)
noexcept
421 typedef BOOL(WINAPI *PFNRoOriginateLanguageException)(int32_t,
void *,
void *);
422 static PFNRoOriginateLanguageException RoOriginateLanguageException = _combase.GetFunction(
"RoOriginateLanguageException");
424 if (RoOriginateLanguageException !=
nullptr) {
425 return RoOriginateLanguageException(error, message, languageException);
431extern "C" int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(
void *classId, winrt::guid
const &iid,
void **factory)
noexcept
433 typedef BOOL(WINAPI *PFNRoGetActivationFactory)(
void *, winrt::guid
const &,
void **);
434 static PFNRoGetActivationFactory RoGetActivationFactory = _combase.GetFunction(
"RoGetActivationFactory");
436 if (RoGetActivationFactory !=
nullptr) {
437 return RoGetActivationFactory(classId, iid, factory);
440 return winrt::impl::error_class_not_available;
445static bool IsDarkModeEnabled()
448#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
449 if (IsWindows10OrGreater()) {
459 winrt::Windows::UI::ViewManagement::UISettings
settings;
460 auto foreground =
settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
463 return ((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128);
474static void SetDarkModeForWindow(HWND hWnd,
bool dark_mode)
477#if defined(NTDDI_WIN10)
478 if (!IsWindows10OrGreater())
return;
483 typedef HRESULT(WINAPI *PFNDWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD);
484 static const PFNDWMSETWINDOWATTRIBUTE DwmSetWindowAttribute = _dwmapi.GetFunction(
"DwmSetWindowAttribute");
486 if (DwmSetWindowAttribute !=
nullptr) {
490 BOOL value = dark_mode ? TRUE : FALSE;
491 if (DwmSetWindowAttribute(hWnd, 20 , &value,
sizeof(value)) != S_OK) {
492 DwmSetWindowAttribute(hWnd, 19 , &value,
sizeof(value));
498LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
500 static uint32_t keycode = 0;
501 static bool console =
false;
503 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f / WHEEL_DELTA;
509 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
515 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
518 case WM_SETTINGCHANGE:
520 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
525 GetUpdateRect(hwnd, &r, FALSE);
526 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
528 ValidateRect(hwnd,
nullptr);
532 case WM_PALETTECHANGED:
533 if ((HWND)wParam == hwnd)
return 0;
536 case WM_QUERYNEWPALETTE:
541 HandleExitGameRequest();
582 int x = (int16_t)LOWORD(lParam);
583 int y = (int16_t)HIWORD(lParam);
591 tme.cbSize =
sizeof(tme);
592 tme.dwFlags = TME_LEAVE;
593 tme.hwndTrack = hwnd;
595 TrackMouseEvent(&tme);
602 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
603 x = (int16_t)LOWORD(m.lParam);
604 y = (int16_t)HIWORD(m.lParam);
610 pt.x = _cursor.
pos.
x;
611 pt.y = _cursor.
pos.
y;
612 ClientToScreen(hwnd, &pt);
613 SetCursorPos(pt.x, pt.y);
620 case WM_INPUTLANGCHANGE:
624 case WM_IME_SETCONTEXT:
629 case WM_IME_STARTCOMPOSITION:
634 case WM_IME_COMPOSITION:
637 case WM_IME_ENDCOMPOSITION:
648 console =
GB(lParam, 16, 8) == 41;
652 uint scancode =
GB(lParam, 16, 8);
653 uint charcode = wParam;
657 if (console && scancode == 41) {
664 uint cur_keycode = keycode;
672 uint scancode =
GB(lParam, 16, 8);
673 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
675 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
686 if (
HasBit(charcode, 31) && !console) {
687 if (scancode == 41) {
696 uint cur_keycode = keycode;
726 if (wParam != SIZE_MINIMIZED) {
729 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
730 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
731 video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
736 RECT *r = (RECT*)lParam;
740 SetRect(&r2, 0, 0, 0, 0);
741 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
743 w = r->right - r->left - (r2.right - r2.left);
744 h = r->bottom - r->top - (r2.bottom - r2.top);
747 SetRect(&r2, 0, 0, w, h);
749 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
750 w = r2.right - r2.left;
751 h = r2.bottom - r2.top;
755 r->bottom = r->top + h;
758 case WMSZ_BOTTOMLEFT:
759 r->bottom = r->top + h;
760 r->left = r->right - w;
763 case WMSZ_BOTTOMRIGHT:
764 r->bottom = r->top + h;
765 r->right = r->left + w;
769 r->left = r->right - w;
773 r->right = r->left + w;
777 r->top = r->bottom - h;
781 r->top = r->bottom - h;
782 r->left = r->right - w;
786 r->top = r->bottom - h;
787 r->right = r->left + w;
793 case WM_DPICHANGED: {
797 RECT *prcNewWindow = (RECT *)lParam;
802 prcNewWindow->right - prcNewWindow->left,
803 prcNewWindow->bottom - prcNewWindow->top,
804 SWP_NOZORDER | SWP_NOACTIVATE);
812#if !defined(WM_MOUSEWHEEL)
813# define WM_MOUSEWHEEL 0x020A
815#if !defined(WM_MOUSEHWHEEL)
816# define WM_MOUSEHWHEEL 0x020E
818#if !defined(GET_WHEEL_DELTA_WPARAM)
819# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
822 case WM_MOUSEWHEEL: {
823 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
827 }
else if (delta > 0) {
832 _cursor.wheel_moved =
true;
837 case WM_MOUSEHWHEEL: {
838 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
841 _cursor.wheel_moved =
true;
857 if (_exit_game)
break;
859 bool active = (LOWORD(wParam) != WA_INACTIVE);
860 bool minimized = (HIWORD(wParam) != 0);
862 if (active && minimized) {
865 ShowWindow(hwnd, SW_RESTORE);
868 }
else if (!active && !minimized) {
870 ShowWindow(hwnd, SW_MINIMIZE);
871 ChangeDisplaySettings(
nullptr, 0);
878 return DefWindowProc(hwnd, msg, wParam, lParam);
881static void RegisterWndClass()
883 static bool registered =
false;
885 if (registered)
return;
887 HINSTANCE hinst = GetModuleHandle(
nullptr);
894 LoadIcon(hinst, MAKEINTRESOURCE(100)),
895 LoadCursor(
nullptr, IDC_ARROW),
902 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
905static const Dimension default_resolutions[] = {
919static void FindResolutions(uint8_t bpp)
924 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
925 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
927 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
932 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
938void VideoDriver_Win32Base::Initialize()
956 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
968 this->
MakeDirty(0, 0, _screen.width, _screen.height);
985 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
986 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
987 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
988 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
1000 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
1004 DispatchMessage(&mesg);
1014 if (_exit_game)
break;
1023void VideoDriver_Win32Base::ClientSizeChanged(
int w,
int h,
bool force)
1037 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
1060static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
1062 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
1064 MONITORINFOEX monitorInfo = {};
1065 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
1066 GetMonitorInfo(hMonitor, &monitorInfo);
1068 DEVMODE devMode = {};
1069 devMode.dmSize =
sizeof(DEVMODE);
1070 devMode.dmDriverExtra = 0;
1071 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
1073 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
1079 std::vector<int> rates = {};
1080 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
1086 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
1095 assert(_screen.dst_ptr !=
nullptr);
1102 assert(_screen.dst_ptr !=
nullptr);
1103 if (_screen.dst_ptr !=
nullptr) {
1106 _screen.dst_ptr =
nullptr;
1121 this->MakePalette();
1129 return std::nullopt;
1144 w = std::max(w, 64);
1145 h = std::max(h, 64);
1147 if (!force && w == _screen.width && h == _screen.height)
return false;
1149 BITMAPINFO *bi = (BITMAPINFO *)
new char[
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256]();
1150 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1152 bi->bmiHeader.biWidth = this->
width = w;
1153 bi->bmiHeader.biHeight = -(this->
height = h);
1155 bi->bmiHeader.biPlanes = 1;
1156 bi->bmiHeader.biBitCount = bpp;
1157 bi->bmiHeader.biCompression = BI_RGB;
1162 this->
dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&this->
buffer_bits,
nullptr, 0);
1165 UserError(
"CreateDIBSection failed");
1170 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1184void VideoDriver_Win32GDI::MakePalette()
1188 LOGPALETTE *pal = (LOGPALETTE *)
new char[
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY)]();
1190 pal->palVersion = 0x300;
1191 pal->palNumEntries = 256;
1193 for (uint i = 0; i != 256; i++) {
1197 pal->palPalEntry[i].peFlags = 0;
1202 if (this->
gdi_palette ==
nullptr) UserError(
"CreatePalette failed!\n");
1205void VideoDriver_Win32GDI::UpdatePalette(HDC dc, uint start, uint count)
1209 for (uint i = 0; i != count; i++) {
1213 rgb[i].rgbReserved = 0;
1216 SetDIBColorTable(dc, start, count, rgb);
1221 HDC hDC = GetWindowDC(hWnd);
1222 HPALETTE hOldPalette = SelectPalette(hDC, this->
gdi_palette, FALSE);
1223 UINT nChanged = RealizePalette(hDC);
1225 SelectPalette(hDC, hOldPalette, TRUE);
1226 ReleaseDC(hWnd, hDC);
1227 if (nChanged != 0) this->
MakeDirty(0, 0, _screen.width, _screen.height);
1237 HDC dc2 = CreateCompatibleDC(dc);
1239 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, this->
dib_sect);
1240 HPALETTE old_palette = SelectPalette(dc, this->
gdi_palette, FALSE);
1264 BitBlt(dc, 0, 0, this->
width, this->
height, dc2, 0, 0, SRCCOPY);
1265 SelectPalette(dc, old_palette, TRUE);
1266 SelectObject(dc2, old_bmp);
1277 int VideoDriver_Win32GDI::RedrawScreenDebug()
1295#ifndef PFD_SUPPORT_COMPOSITION
1296# define PFD_SUPPORT_COMPOSITION 0x00008000
1299static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1300static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1301static bool _hasWGLARBCreateContextProfile =
false;
1306 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1307 if (ret ==
nullptr) {
1309 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1319static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1321 PIXELFORMATDESCRIPTOR pfd = {
1322 sizeof(PIXELFORMATDESCRIPTOR),
1324 PFD_DRAW_TO_WINDOW |
1325 PFD_SUPPORT_OPENGL |
1330 0, 0, 0, 0, 0, 0, 0, 0,
1338 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1341 int format = ChoosePixelFormat(dc, &pfd);
1342 if (format == 0)
return "No suitable pixel format found";
1343 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1345 return std::nullopt;
1349static void LoadWGLExtensions()
1356 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1357 HDC dc = GetDC(wnd);
1360 if (SelectPixelFormat(dc) == std::nullopt) {
1362 HGLRC rc = wglCreateContext(dc);
1363 if (rc !=
nullptr) {
1364 wglMakeCurrent(dc, rc);
1368#pragma GCC diagnostic push
1369#pragma GCC diagnostic ignored "-Wcast-function-type"
1373 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1374 if (wglGetExtensionsStringARB !=
nullptr) {
1375 std::string_view wgl_exts = wglGetExtensionsStringARB(dc);
1377 if (HasStringInExtensionList(wgl_exts,
"WGL_ARB_create_context")) {
1378 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1380 _hasWGLARBCreateContextProfile = HasStringInExtensionList(wgl_exts,
"WGL_ARB_create_context_profile");
1381 if (HasStringInExtensionList(wgl_exts,
"WGL_EXT_swap_control")) {
1382 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1387#pragma GCC diagnostic pop
1389 wglMakeCurrent(
nullptr,
nullptr);
1390 wglDeleteContext(rc);
1398static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1400std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1406 LoadWGLExtensions();
1409 this->MakeWindow(_fullscreen);
1412 auto err = this->AllocateContext();
1419 this->driver_info = GetName();
1420 this->driver_info +=
" (";
1422 this->driver_info +=
")";
1424 this->ClientSizeChanged(this->width, this->height,
true);
1426 if (_screen.dst_ptr ==
nullptr) {
1429 return "Can't get pointer to screen buffer";
1432 this->ReleaseVideoPointer();
1438 return std::nullopt;
1441void VideoDriver_Win32OpenGL::Stop()
1443 this->DestroyContext();
1447void VideoDriver_Win32OpenGL::DestroyContext()
1451 wglMakeCurrent(
nullptr,
nullptr);
1452 if (this->gl_rc !=
nullptr) {
1453 wglDeleteContext(this->gl_rc);
1454 this->gl_rc =
nullptr;
1456 if (this->dc !=
nullptr) {
1457 ReleaseDC(this->main_wnd, this->dc);
1462void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1464 if (_wglSwapIntervalEXT !=
nullptr) {
1465 _wglSwapIntervalEXT(vsync);
1467 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1471std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1473 this->dc = GetDC(this->main_wnd);
1475 auto err = SelectPixelFormat(this->dc);
1476 if (err)
return err;
1481 if (_wglCreateContextAttribsARB !=
nullptr) {
1484 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1485 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1486 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1487 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1490 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1492 if (rc ==
nullptr) {
1496 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1500 if (rc ==
nullptr) {
1502 rc = wglCreateContext(this->dc);
1503 if (rc ==
nullptr)
return "Can't create OpenGL context";
1505 if (!wglMakeCurrent(this->dc, rc))
return "Can't activate GL context";
1513bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1515 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1516 this->DestroyContext();
1518 res &= this->AllocateContext() == std::nullopt;
1519 this->ClientSizeChanged(this->width, this->height,
true);
1523bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1526 this->ClientSizeChanged(this->width, this->height,
true);
1530void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1535void VideoDriver_Win32OpenGL::ClearSystemSprites()
1540bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1542 if (!force && w == _screen.width && h == _screen.height)
return false;
1544 this->width = w = std::max(w, 64);
1545 this->height = h = std::max(h, 64);
1547 if (this->gl_rc ==
nullptr)
return false;
1549 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1551 this->dirty_rect = {};
1553 SwapBuffers(this->dc);
1554 _screen.dst_ptr = this->GetVideoPointer();
1559void *VideoDriver_Win32OpenGL::GetVideoPointer()
1567void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1571 this->dirty_rect = {};
1572 _screen.dst_ptr =
nullptr;
1573 this->anim_buffer =
nullptr;
1576void VideoDriver_Win32OpenGL::Paint()
1595 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.
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.
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!
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: