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"
29#include <versionhelpers.h>
30#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
31#include <winrt/Windows.UI.ViewManagement.h>
34#include "../safeguards.h"
37#ifndef MAPVK_VK_TO_CHAR
38#define MAPVK_VK_TO_CHAR (2)
42#define PM_QS_INPUT 0x20000
46#define WM_DPICHANGED 0x02E0
55bool VideoDriver_Win32Base::ClaimMousePointer()
57 MyShowCursor(
false,
true);
67#define AS(x, z) {x, 1, z}
68#define AM(x, y, z, w) {x, y - x + 1, z}
72 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
74 AM(
'A',
'Z',
'A',
'Z'),
75 AM(
'0',
'9',
'0',
'9'),
77 AS(VK_ESCAPE, WKC_ESC),
78 AS(VK_PAUSE, WKC_PAUSE),
79 AS(VK_BACK, WKC_BACKSPACE),
80 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
82 AS(VK_SPACE, WKC_SPACE),
83 AS(VK_RETURN, WKC_RETURN),
87 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
90 AM(VK_NUMPAD0, VK_NUMPAD9,
'0',
'9'),
91 AS(VK_DIVIDE, WKC_NUM_DIV),
92 AS(VK_MULTIPLY, WKC_NUM_MUL),
93 AS(VK_SUBTRACT, WKC_NUM_MINUS),
94 AS(VK_ADD, WKC_NUM_PLUS),
95 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
111static uint MapWindowsKey(uint sym)
115 for (
const auto &map : _vk_mapping) {
116 if (
IsInsideBS(sym, map.vk_from, map.vk_count)) {
117 key = sym - map.vk_from + map.map_to;
122 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
123 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
124 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
145 _fullscreen = full_screen;
167 if (settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
172 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
174 GetWindowRect(GetDesktopWindow(), &r);
177 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
182 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
188 ChangeDisplaySettings(
nullptr, 0);
190 this->
width = _bck_resolution.width;
191 this->
height = _bck_resolution.height;
196 DWORD style, showstyle;
199 showstyle = SW_SHOWNORMAL;
205 style = WS_OVERLAPPEDWINDOW;
207 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
211 AdjustWindowRect(&r, style, FALSE);
212 w = r.right - r.left;
213 h = r.bottom - r.top;
216 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
224 mi.cbSize =
sizeof(mi);
225 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
227 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
228 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
232 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
233 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
234 ShowWindow(this->
main_wnd, showstyle);
247 static char32_t prev_char = 0;
251 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
252 prev_char = charcode;
257 if (prev_char != 0) {
261 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
274 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
280 HIMC hIMC = ImmGetContext(hwnd);
281 if (hIMC !=
nullptr) {
283 cf.dwStyle = CFS_POINT;
288 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
289 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
291 cf.ptCurrentPos.x = 0;
292 cf.ptCurrentPos.y = 0;
294 ImmSetCompositionWindow(hIMC, &cf);
296 ImmReleaseContext(hwnd, hIMC);
302 HIMC hIMC = ImmGetContext(hwnd);
303 if (hIMC !=
nullptr) {
306 cf.dwStyle = CFS_EXCLUDE;
310 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
311 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
313 cf.rcArea.left = _focused_window->
left;
314 cf.rcArea.top = _focused_window->
top;
315 cf.rcArea.right = _focused_window->
left + _focused_window->
width;
316 cf.rcArea.bottom = _focused_window->
top + _focused_window->
height;
324 cf.ptCurrentPos.x = 0;
325 cf.ptCurrentPos.y = 0;
326 SetRectEmpty(&cf.rcArea);
328 ImmSetCandidateWindow(hIMC, &cf);
330 ImmReleaseContext(hwnd, hIMC);
336 HIMC hIMC = ImmGetContext(hwnd);
338 if (hIMC !=
nullptr) {
339 if (lParam & GCS_RESULTSTR) {
341 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
342 std::wstring str(len + 1, L
'\0');
343 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
344 str[len /
sizeof(wchar_t)] = L
'\0';
354 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
359 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
360 std::wstring str(len + 1, L
'\0');
361 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
362 str[len /
sizeof(wchar_t)] = L
'\0';
365 static char utf8_buf[1024];
369 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
370 const char *caret = utf8_buf;
371 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && *caret !=
'\0' && caret_bytes > 0; c++, caret_bytes--) {
385 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
388 ImmReleaseContext(hwnd, hIMC);
390 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
396 HIMC hIMC = ImmGetContext(hwnd);
397 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
398 ImmReleaseContext(hwnd, hIMC);
403static bool IsDarkModeEnabled()
406#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
407 if (IsWindows10OrGreater()) {
417 winrt::Windows::UI::ViewManagement::UISettings
settings;
418 auto foreground =
settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
421 return ((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128);
432static void SetDarkModeForWindow(HWND hWnd,
bool dark_mode)
435#if defined(NTDDI_WIN10)
436 if (!IsWindows10OrGreater())
return;
441 typedef HRESULT(WINAPI *PFNDWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD);
442 static PFNDWMSETWINDOWATTRIBUTE DwmSetWindowAttribute = _dwmapi.GetFunction(
"DwmSetWindowAttribute");
444 if (DwmSetWindowAttribute !=
nullptr) {
448 BOOL value = dark_mode ? TRUE : FALSE;
449 if (DwmSetWindowAttribute(hWnd, 20 , &value,
sizeof(value)) != S_OK) {
450 DwmSetWindowAttribute(hWnd, 19 , &value,
sizeof(value));
456LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
458 static uint32_t keycode = 0;
459 static bool console =
false;
461 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f / WHEEL_DELTA;
467 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
473 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
476 case WM_SETTINGCHANGE:
478 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
483 GetUpdateRect(hwnd, &r, FALSE);
484 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
486 ValidateRect(hwnd,
nullptr);
490 case WM_PALETTECHANGED:
491 if ((HWND)wParam == hwnd)
return 0;
494 case WM_QUERYNEWPALETTE:
499 HandleExitGameRequest();
540 int x = (int16_t)LOWORD(lParam);
541 int y = (int16_t)HIWORD(lParam);
549 tme.cbSize =
sizeof(tme);
550 tme.dwFlags = TME_LEAVE;
551 tme.hwndTrack = hwnd;
553 TrackMouseEvent(&tme);
560 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
561 x = (int16_t)LOWORD(m.lParam);
562 y = (int16_t)HIWORD(m.lParam);
568 pt.x = _cursor.
pos.x;
569 pt.y = _cursor.
pos.y;
570 ClientToScreen(hwnd, &pt);
571 SetCursorPos(pt.x, pt.y);
578 case WM_INPUTLANGCHANGE:
582 case WM_IME_SETCONTEXT:
587 case WM_IME_STARTCOMPOSITION:
592 case WM_IME_COMPOSITION:
595 case WM_IME_ENDCOMPOSITION:
606 console =
GB(lParam, 16, 8) == 41;
610 uint scancode =
GB(lParam, 16, 8);
611 uint charcode = wParam;
615 if (console && scancode == 41) {
622 uint cur_keycode = keycode;
630 uint scancode =
GB(lParam, 16, 8);
631 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
633 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
644 if (
HasBit(charcode, 31) && !console) {
645 if (scancode == 41) {
654 uint cur_keycode = keycode;
684 if (wParam != SIZE_MINIMIZED) {
687 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
688 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
689 video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
694 RECT *r = (RECT*)lParam;
698 SetRect(&r2, 0, 0, 0, 0);
699 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
701 w = r->right - r->left - (r2.right - r2.left);
702 h = r->bottom - r->top - (r2.bottom - r2.top);
705 SetRect(&r2, 0, 0, w, h);
707 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
708 w = r2.right - r2.left;
709 h = r2.bottom - r2.top;
713 r->bottom = r->top + h;
716 case WMSZ_BOTTOMLEFT:
717 r->bottom = r->top + h;
718 r->left = r->right - w;
721 case WMSZ_BOTTOMRIGHT:
722 r->bottom = r->top + h;
723 r->right = r->left + w;
727 r->left = r->right - w;
731 r->right = r->left + w;
735 r->top = r->bottom - h;
739 r->top = r->bottom - h;
740 r->left = r->right - w;
744 r->top = r->bottom - h;
745 r->right = r->left + w;
751 case WM_DPICHANGED: {
755 RECT *prcNewWindow = (RECT *)lParam;
760 prcNewWindow->right - prcNewWindow->left,
761 prcNewWindow->bottom - prcNewWindow->top,
762 SWP_NOZORDER | SWP_NOACTIVATE);
770#if !defined(WM_MOUSEWHEEL)
771# define WM_MOUSEWHEEL 0x020A
773#if !defined(WM_MOUSEHWHEEL)
774# define WM_MOUSEHWHEEL 0x020E
776#if !defined(GET_WHEEL_DELTA_WPARAM)
777# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
780 case WM_MOUSEWHEEL: {
781 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
785 }
else if (delta > 0) {
790 _cursor.wheel_moved =
true;
795 case WM_MOUSEHWHEEL: {
796 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
799 _cursor.wheel_moved =
true;
815 if (_exit_game)
break;
817 bool active = (LOWORD(wParam) != WA_INACTIVE);
818 bool minimized = (HIWORD(wParam) != 0);
820 if (active && minimized) {
823 ShowWindow(hwnd, SW_RESTORE);
826 }
else if (!active && !minimized) {
828 ShowWindow(hwnd, SW_MINIMIZE);
829 ChangeDisplaySettings(
nullptr, 0);
836 return DefWindowProc(hwnd, msg, wParam, lParam);
839static void RegisterWndClass()
841 static bool registered =
false;
843 if (registered)
return;
845 HINSTANCE hinst = GetModuleHandle(
nullptr);
852 LoadIcon(hinst, MAKEINTRESOURCE(100)),
853 LoadCursor(
nullptr, IDC_ARROW),
860 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
863static const Dimension default_resolutions[] = {
877static void FindResolutions(uint8_t bpp)
882 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
883 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
885 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
890 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
896void VideoDriver_Win32Base::Initialize()
914 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
926 this->
MakeDirty(0, 0, _screen.width, _screen.height);
943 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
944 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
945 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
946 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
958 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
962 DispatchMessage(&mesg);
972 if (_exit_game)
break;
981void VideoDriver_Win32Base::ClientSizeChanged(
int w,
int h,
bool force)
995 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
1018static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
1020 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
1022 MONITORINFOEX monitorInfo = {};
1023 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
1024 GetMonitorInfo(hMonitor, &monitorInfo);
1026 DEVMODE devMode = {};
1027 devMode.dmSize =
sizeof(DEVMODE);
1028 devMode.dmDriverExtra = 0;
1029 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
1031 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
1037 std::vector<int> rates = {};
1038 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
1044 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
1049 typedef UINT (WINAPI *PFNGETDPIFORWINDOW)(HWND hwnd);
1050 typedef UINT (WINAPI *PFNGETDPIFORSYSTEM)(VOID);
1051 typedef HRESULT (WINAPI *PFNGETDPIFORMONITOR)(HMONITOR hMonitor,
int dpiType, UINT *dpiX, UINT *dpiY);
1053 static PFNGETDPIFORWINDOW _GetDpiForWindow =
nullptr;
1054 static PFNGETDPIFORSYSTEM _GetDpiForSystem =
nullptr;
1055 static PFNGETDPIFORMONITOR _GetDpiForMonitor =
nullptr;
1057 static bool init_done =
false;
1062 _GetDpiForWindow = _user32.
GetFunction(
"GetDpiForWindow");
1063 _GetDpiForSystem = _user32.
GetFunction(
"GetDpiForSystem");
1064 _GetDpiForMonitor = _shcore.
GetFunction(
"GetDpiForMonitor");
1069 if (cur_dpi == 0 && _GetDpiForWindow !=
nullptr && this->
main_wnd !=
nullptr) {
1071 cur_dpi = _GetDpiForWindow(this->
main_wnd);
1073 if (cur_dpi == 0 && _GetDpiForMonitor !=
nullptr && this->
main_wnd !=
nullptr) {
1076 if (SUCCEEDED(_GetDpiForMonitor(MonitorFromWindow(this->
main_wnd, MONITOR_DEFAULTTOPRIMARY), 0 , &dpiX, &dpiY))) {
1080 if (cur_dpi == 0 && _GetDpiForSystem !=
nullptr) {
1082 cur_dpi = _GetDpiForSystem();
1085 return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f;
1094 assert(_screen.dst_ptr !=
nullptr);
1101 assert(_screen.dst_ptr !=
nullptr);
1102 if (_screen.dst_ptr !=
nullptr) {
1105 _screen.dst_ptr =
nullptr;
1120 this->MakePalette();
1128 return std::nullopt;
1143 w = std::max(w, 64);
1144 h = std::max(h, 64);
1146 if (!force && w == _screen.width && h == _screen.height)
return false;
1148 BITMAPINFO *bi = (BITMAPINFO *)
new char[
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256]();
1149 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1151 bi->bmiHeader.biWidth = this->
width = w;
1152 bi->bmiHeader.biHeight = -(this->
height = h);
1154 bi->bmiHeader.biPlanes = 1;
1155 bi->bmiHeader.biBitCount = bpp;
1156 bi->bmiHeader.biCompression = BI_RGB;
1161 this->
dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&this->
buffer_bits,
nullptr, 0);
1164 UserError(
"CreateDIBSection failed");
1169 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1183void VideoDriver_Win32GDI::MakePalette()
1187 LOGPALETTE *pal = (LOGPALETTE *)
new char[
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY)]();
1189 pal->palVersion = 0x300;
1190 pal->palNumEntries = 256;
1192 for (uint i = 0; i != 256; i++) {
1196 pal->palPalEntry[i].peFlags = 0;
1201 if (this->
gdi_palette ==
nullptr) UserError(
"CreatePalette failed!\n");
1204void VideoDriver_Win32GDI::UpdatePalette(HDC dc, uint start, uint count)
1208 for (uint i = 0; i != count; i++) {
1212 rgb[i].rgbReserved = 0;
1215 SetDIBColorTable(dc, start, count, rgb);
1220 HDC hDC = GetWindowDC(hWnd);
1221 HPALETTE hOldPalette = SelectPalette(hDC, this->
gdi_palette, FALSE);
1222 UINT nChanged = RealizePalette(hDC);
1224 SelectPalette(hDC, hOldPalette, TRUE);
1225 ReleaseDC(hWnd, hDC);
1226 if (nChanged != 0) this->
MakeDirty(0, 0, _screen.width, _screen.height);
1236 HDC dc2 = CreateCompatibleDC(dc);
1238 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, this->
dib_sect);
1239 HPALETTE old_palette = SelectPalette(dc, this->
gdi_palette, FALSE);
1263 BitBlt(dc, 0, 0, this->
width, this->
height, dc2, 0, 0, SRCCOPY);
1264 SelectPalette(dc, old_palette, TRUE);
1265 SelectObject(dc2, old_bmp);
1276 int VideoDriver_Win32GDI::RedrawScreenDebug()
1295#include "../3rdparty/opengl/glext.h"
1296#include "../3rdparty/opengl/wglext.h"
1299#ifndef PFD_SUPPORT_COMPOSITION
1300# define PFD_SUPPORT_COMPOSITION 0x00008000
1303static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1304static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1305static bool _hasWGLARBCreateContextProfile =
false;
1310 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1311 if (ret ==
nullptr) {
1313 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1323static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1325 PIXELFORMATDESCRIPTOR pfd = {
1326 sizeof(PIXELFORMATDESCRIPTOR),
1328 PFD_DRAW_TO_WINDOW |
1329 PFD_SUPPORT_OPENGL |
1334 0, 0, 0, 0, 0, 0, 0, 0,
1342 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1345 int format = ChoosePixelFormat(dc, &pfd);
1346 if (format == 0)
return "No suitable pixel format found";
1347 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1349 return std::nullopt;
1353static void LoadWGLExtensions()
1360 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1361 HDC dc = GetDC(wnd);
1364 if (SelectPixelFormat(dc) == std::nullopt) {
1366 HGLRC rc = wglCreateContext(dc);
1367 if (rc !=
nullptr) {
1368 wglMakeCurrent(dc, rc);
1372#pragma GCC diagnostic push
1373#pragma GCC diagnostic ignored "-Wcast-function-type"
1377 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1378 if (wglGetExtensionsStringARB !=
nullptr) {
1379 const char *wgl_exts = wglGetExtensionsStringARB(dc);
1381 if (FindStringInExtensionList(wgl_exts,
"WGL_ARB_create_context") !=
nullptr) {
1382 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1384 _hasWGLARBCreateContextProfile = FindStringInExtensionList(wgl_exts,
"WGL_ARB_create_context_profile") !=
nullptr;
1385 if (FindStringInExtensionList(wgl_exts,
"WGL_EXT_swap_control") !=
nullptr) {
1386 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1391#pragma GCC diagnostic pop
1393 wglMakeCurrent(
nullptr,
nullptr);
1394 wglDeleteContext(rc);
1402static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1404std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1410 LoadWGLExtensions();
1413 this->MakeWindow(_fullscreen);
1416 auto err = this->AllocateContext();
1423 this->driver_info = GetName();
1424 this->driver_info +=
" (";
1426 this->driver_info +=
")";
1428 this->ClientSizeChanged(this->width, this->height,
true);
1430 if (_screen.dst_ptr ==
nullptr) {
1433 return "Can't get pointer to screen buffer";
1436 this->ReleaseVideoPointer();
1442 return std::nullopt;
1445void VideoDriver_Win32OpenGL::Stop()
1447 this->DestroyContext();
1451void VideoDriver_Win32OpenGL::DestroyContext()
1455 wglMakeCurrent(
nullptr,
nullptr);
1456 if (this->gl_rc !=
nullptr) {
1457 wglDeleteContext(this->gl_rc);
1458 this->gl_rc =
nullptr;
1460 if (this->dc !=
nullptr) {
1461 ReleaseDC(this->main_wnd, this->dc);
1466void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1468 if (_wglSwapIntervalEXT !=
nullptr) {
1469 _wglSwapIntervalEXT(vsync);
1471 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1475std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1477 this->dc = GetDC(this->main_wnd);
1479 auto err = SelectPixelFormat(this->dc);
1480 if (err)
return err;
1485 if (_wglCreateContextAttribsARB !=
nullptr) {
1488 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1489 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1490 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1491 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1494 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1496 if (rc ==
nullptr) {
1500 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1504 if (rc ==
nullptr) {
1506 rc = wglCreateContext(this->dc);
1507 if (rc ==
nullptr)
return "Can't create OpenGL context";
1509 if (!wglMakeCurrent(this->dc, rc))
return "Can't active GL context";
1517bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1519 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1520 this->DestroyContext();
1522 res &= this->AllocateContext() == std::nullopt;
1523 this->ClientSizeChanged(this->width, this->height,
true);
1527bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1530 this->ClientSizeChanged(this->width, this->height,
true);
1534void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1539void VideoDriver_Win32OpenGL::ClearSystemSprites()
1544bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1546 if (!force && w == _screen.width && h == _screen.height)
return false;
1548 this->width = w = std::max(w, 64);
1549 this->height = h = std::max(h, 64);
1551 if (this->gl_rc ==
nullptr)
return false;
1553 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1555 this->dirty_rect = {};
1557 SwapBuffers(this->dc);
1558 _screen.dst_ptr = this->GetVideoPointer();
1563void *VideoDriver_Win32OpenGL::GetVideoPointer()
1571void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1575 this->dirty_rect = {};
1576 _screen.dst_ptr =
nullptr;
1577 this->anim_buffer =
nullptr;
1580void VideoDriver_Win32OpenGL::Paint()
1599 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.
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,...)
Ouptut a line of debugging information.
std::vector< Dimension > _resolutions
List of resolutions.
Dimension _cur_resolution
The current resolution.
bool GetDriverParamBool(const StringList &parm, const char *name)
Get a boolean parameter the list of parameters.
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(const char *str, bool marked=false, const char *caret=nullptr, const char *insert_location=nullptr, const char *replacement_end=nullptr)
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 funtion 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::wstring OTTD2FS(const std::string &name)
Convert from OpenTTD's encoding to a wide string.
char * 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::string FS2OTTD(const std::wstring &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: