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;
168 if (settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
173 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
175 GetWindowRect(GetDesktopWindow(), &r);
178 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
183 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
189 ChangeDisplaySettings(
nullptr, 0);
191 this->
width = _bck_resolution.width;
192 this->
height = _bck_resolution.height;
197 DWORD style, showstyle;
200 showstyle = SW_SHOWNORMAL;
206 style = WS_OVERLAPPEDWINDOW;
208 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
212 AdjustWindowRect(&r, style, FALSE);
213 w = r.right - r.left;
214 h = r.bottom - r.top;
217 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
225 mi.cbSize =
sizeof(mi);
226 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
228 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
229 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
233 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
234 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
235 ShowWindow(this->
main_wnd, showstyle);
248 static char32_t prev_char = 0;
252 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
253 prev_char = charcode;
258 if (prev_char != 0) {
262 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
275 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
281 HIMC hIMC = ImmGetContext(hwnd);
282 if (hIMC !=
nullptr) {
284 cf.dwStyle = CFS_POINT;
289 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
290 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
292 cf.ptCurrentPos.x = 0;
293 cf.ptCurrentPos.y = 0;
295 ImmSetCompositionWindow(hIMC, &cf);
297 ImmReleaseContext(hwnd, hIMC);
303 HIMC hIMC = ImmGetContext(hwnd);
304 if (hIMC !=
nullptr) {
307 cf.dwStyle = CFS_EXCLUDE;
311 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
312 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
314 cf.rcArea.left = _focused_window->
left;
315 cf.rcArea.top = _focused_window->
top;
316 cf.rcArea.right = _focused_window->
left + _focused_window->
width;
317 cf.rcArea.bottom = _focused_window->
top + _focused_window->
height;
325 cf.ptCurrentPos.x = 0;
326 cf.ptCurrentPos.y = 0;
327 SetRectEmpty(&cf.rcArea);
329 ImmSetCandidateWindow(hIMC, &cf);
331 ImmReleaseContext(hwnd, hIMC);
337 HIMC hIMC = ImmGetContext(hwnd);
339 if (hIMC !=
nullptr) {
340 if (lParam & GCS_RESULTSTR) {
342 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
343 std::wstring str(len + 1, L
'\0');
344 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
345 str[len /
sizeof(wchar_t)] = L
'\0';
355 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
360 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
361 std::wstring str(len + 1, L
'\0');
362 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
363 str[len /
sizeof(wchar_t)] = L
'\0';
366 static char utf8_buf[1024];
370 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
372 auto caret = view.begin();
373 const auto end = view.end();
374 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && caret != end && caret_bytes > 0; c++, caret_bytes--) {
388 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
391 ImmReleaseContext(hwnd, hIMC);
393 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
399 HIMC hIMC = ImmGetContext(hwnd);
400 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
401 ImmReleaseContext(hwnd, hIMC);
406static bool IsDarkModeEnabled()
409#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
410 if (IsWindows10OrGreater()) {
420 winrt::Windows::UI::ViewManagement::UISettings
settings;
421 auto foreground =
settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
424 return ((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128);
435static void SetDarkModeForWindow(HWND hWnd,
bool dark_mode)
438#if defined(NTDDI_WIN10)
439 if (!IsWindows10OrGreater())
return;
444 typedef HRESULT(WINAPI *PFNDWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD);
445 static PFNDWMSETWINDOWATTRIBUTE DwmSetWindowAttribute = _dwmapi.GetFunction(
"DwmSetWindowAttribute");
447 if (DwmSetWindowAttribute !=
nullptr) {
451 BOOL value = dark_mode ? TRUE : FALSE;
452 if (DwmSetWindowAttribute(hWnd, 20 , &value,
sizeof(value)) != S_OK) {
453 DwmSetWindowAttribute(hWnd, 19 , &value,
sizeof(value));
459LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
461 static uint32_t keycode = 0;
462 static bool console =
false;
464 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f / WHEEL_DELTA;
470 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
476 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
479 case WM_SETTINGCHANGE:
481 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
486 GetUpdateRect(hwnd, &r, FALSE);
487 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
489 ValidateRect(hwnd,
nullptr);
493 case WM_PALETTECHANGED:
494 if ((HWND)wParam == hwnd)
return 0;
497 case WM_QUERYNEWPALETTE:
502 HandleExitGameRequest();
543 int x = (int16_t)LOWORD(lParam);
544 int y = (int16_t)HIWORD(lParam);
552 tme.cbSize =
sizeof(tme);
553 tme.dwFlags = TME_LEAVE;
554 tme.hwndTrack = hwnd;
556 TrackMouseEvent(&tme);
563 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
564 x = (int16_t)LOWORD(m.lParam);
565 y = (int16_t)HIWORD(m.lParam);
571 pt.x = _cursor.
pos.x;
572 pt.y = _cursor.
pos.y;
573 ClientToScreen(hwnd, &pt);
574 SetCursorPos(pt.x, pt.y);
581 case WM_INPUTLANGCHANGE:
585 case WM_IME_SETCONTEXT:
590 case WM_IME_STARTCOMPOSITION:
595 case WM_IME_COMPOSITION:
598 case WM_IME_ENDCOMPOSITION:
609 console =
GB(lParam, 16, 8) == 41;
613 uint scancode =
GB(lParam, 16, 8);
614 uint charcode = wParam;
618 if (console && scancode == 41) {
625 uint cur_keycode = keycode;
633 uint scancode =
GB(lParam, 16, 8);
634 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
636 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
647 if (
HasBit(charcode, 31) && !console) {
648 if (scancode == 41) {
657 uint cur_keycode = keycode;
687 if (wParam != SIZE_MINIMIZED) {
690 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
691 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
692 video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
697 RECT *r = (RECT*)lParam;
701 SetRect(&r2, 0, 0, 0, 0);
702 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
704 w = r->right - r->left - (r2.right - r2.left);
705 h = r->bottom - r->top - (r2.bottom - r2.top);
708 SetRect(&r2, 0, 0, w, h);
710 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
711 w = r2.right - r2.left;
712 h = r2.bottom - r2.top;
716 r->bottom = r->top + h;
719 case WMSZ_BOTTOMLEFT:
720 r->bottom = r->top + h;
721 r->left = r->right - w;
724 case WMSZ_BOTTOMRIGHT:
725 r->bottom = r->top + h;
726 r->right = r->left + w;
730 r->left = r->right - w;
734 r->right = r->left + w;
738 r->top = r->bottom - h;
742 r->top = r->bottom - h;
743 r->left = r->right - w;
747 r->top = r->bottom - h;
748 r->right = r->left + w;
754 case WM_DPICHANGED: {
758 RECT *prcNewWindow = (RECT *)lParam;
763 prcNewWindow->right - prcNewWindow->left,
764 prcNewWindow->bottom - prcNewWindow->top,
765 SWP_NOZORDER | SWP_NOACTIVATE);
773#if !defined(WM_MOUSEWHEEL)
774# define WM_MOUSEWHEEL 0x020A
776#if !defined(WM_MOUSEHWHEEL)
777# define WM_MOUSEHWHEEL 0x020E
779#if !defined(GET_WHEEL_DELTA_WPARAM)
780# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
783 case WM_MOUSEWHEEL: {
784 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
788 }
else if (delta > 0) {
793 _cursor.wheel_moved =
true;
798 case WM_MOUSEHWHEEL: {
799 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
802 _cursor.wheel_moved =
true;
818 if (_exit_game)
break;
820 bool active = (LOWORD(wParam) != WA_INACTIVE);
821 bool minimized = (HIWORD(wParam) != 0);
823 if (active && minimized) {
826 ShowWindow(hwnd, SW_RESTORE);
829 }
else if (!active && !minimized) {
831 ShowWindow(hwnd, SW_MINIMIZE);
832 ChangeDisplaySettings(
nullptr, 0);
839 return DefWindowProc(hwnd, msg, wParam, lParam);
842static void RegisterWndClass()
844 static bool registered =
false;
846 if (registered)
return;
848 HINSTANCE hinst = GetModuleHandle(
nullptr);
855 LoadIcon(hinst, MAKEINTRESOURCE(100)),
856 LoadCursor(
nullptr, IDC_ARROW),
863 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
866static const Dimension default_resolutions[] = {
880static void FindResolutions(uint8_t bpp)
885 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
886 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
888 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
893 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
899void VideoDriver_Win32Base::Initialize()
917 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
929 this->
MakeDirty(0, 0, _screen.width, _screen.height);
946 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
947 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
948 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
949 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
961 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
965 DispatchMessage(&mesg);
975 if (_exit_game)
break;
984void VideoDriver_Win32Base::ClientSizeChanged(
int w,
int h,
bool force)
998 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
1021static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
1023 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
1025 MONITORINFOEX monitorInfo = {};
1026 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
1027 GetMonitorInfo(hMonitor, &monitorInfo);
1029 DEVMODE devMode = {};
1030 devMode.dmSize =
sizeof(DEVMODE);
1031 devMode.dmDriverExtra = 0;
1032 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
1034 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
1040 std::vector<int> rates = {};
1041 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
1047 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
1052 typedef UINT (WINAPI *PFNGETDPIFORWINDOW)(HWND hwnd);
1053 typedef UINT (WINAPI *PFNGETDPIFORSYSTEM)(VOID);
1054 typedef HRESULT (WINAPI *PFNGETDPIFORMONITOR)(HMONITOR hMonitor,
int dpiType, UINT *dpiX, UINT *dpiY);
1056 static PFNGETDPIFORWINDOW _GetDpiForWindow =
nullptr;
1057 static PFNGETDPIFORSYSTEM _GetDpiForSystem =
nullptr;
1058 static PFNGETDPIFORMONITOR _GetDpiForMonitor =
nullptr;
1060 static bool init_done =
false;
1065 _GetDpiForWindow = _user32.
GetFunction(
"GetDpiForWindow");
1066 _GetDpiForSystem = _user32.
GetFunction(
"GetDpiForSystem");
1067 _GetDpiForMonitor = _shcore.
GetFunction(
"GetDpiForMonitor");
1072 if (cur_dpi == 0 && _GetDpiForWindow !=
nullptr && this->
main_wnd !=
nullptr) {
1074 cur_dpi = _GetDpiForWindow(this->
main_wnd);
1076 if (cur_dpi == 0 && _GetDpiForMonitor !=
nullptr && this->
main_wnd !=
nullptr) {
1079 if (SUCCEEDED(_GetDpiForMonitor(MonitorFromWindow(this->
main_wnd, MONITOR_DEFAULTTOPRIMARY), 0 , &dpiX, &dpiY))) {
1083 if (cur_dpi == 0 && _GetDpiForSystem !=
nullptr) {
1085 cur_dpi = _GetDpiForSystem();
1088 return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f;
1097 assert(_screen.dst_ptr !=
nullptr);
1104 assert(_screen.dst_ptr !=
nullptr);
1105 if (_screen.dst_ptr !=
nullptr) {
1108 _screen.dst_ptr =
nullptr;
1123 this->MakePalette();
1131 return std::nullopt;
1146 w = std::max(w, 64);
1147 h = std::max(h, 64);
1149 if (!force && w == _screen.width && h == _screen.height)
return false;
1151 BITMAPINFO *bi = (BITMAPINFO *)
new char[
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256]();
1152 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1154 bi->bmiHeader.biWidth = this->
width = w;
1155 bi->bmiHeader.biHeight = -(this->
height = h);
1157 bi->bmiHeader.biPlanes = 1;
1158 bi->bmiHeader.biBitCount = bpp;
1159 bi->bmiHeader.biCompression = BI_RGB;
1164 this->
dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&this->
buffer_bits,
nullptr, 0);
1167 UserError(
"CreateDIBSection failed");
1172 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1186void VideoDriver_Win32GDI::MakePalette()
1190 LOGPALETTE *pal = (LOGPALETTE *)
new char[
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY)]();
1192 pal->palVersion = 0x300;
1193 pal->palNumEntries = 256;
1195 for (uint i = 0; i != 256; i++) {
1199 pal->palPalEntry[i].peFlags = 0;
1204 if (this->
gdi_palette ==
nullptr) UserError(
"CreatePalette failed!\n");
1207void VideoDriver_Win32GDI::UpdatePalette(HDC dc, uint start, uint count)
1211 for (uint i = 0; i != count; i++) {
1215 rgb[i].rgbReserved = 0;
1218 SetDIBColorTable(dc, start, count, rgb);
1223 HDC hDC = GetWindowDC(hWnd);
1224 HPALETTE hOldPalette = SelectPalette(hDC, this->
gdi_palette, FALSE);
1225 UINT nChanged = RealizePalette(hDC);
1227 SelectPalette(hDC, hOldPalette, TRUE);
1228 ReleaseDC(hWnd, hDC);
1229 if (nChanged != 0) this->
MakeDirty(0, 0, _screen.width, _screen.height);
1239 HDC dc2 = CreateCompatibleDC(dc);
1241 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, this->
dib_sect);
1242 HPALETTE old_palette = SelectPalette(dc, this->
gdi_palette, FALSE);
1266 BitBlt(dc, 0, 0, this->
width, this->
height, dc2, 0, 0, SRCCOPY);
1267 SelectPalette(dc, old_palette, TRUE);
1268 SelectObject(dc2, old_bmp);
1279 int VideoDriver_Win32GDI::RedrawScreenDebug()
1298#include "../3rdparty/opengl/glext.h"
1299#include "../3rdparty/opengl/wglext.h"
1302#ifndef PFD_SUPPORT_COMPOSITION
1303# define PFD_SUPPORT_COMPOSITION 0x00008000
1306static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1307static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1308static bool _hasWGLARBCreateContextProfile =
false;
1313 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1314 if (ret ==
nullptr) {
1316 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1326static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1328 PIXELFORMATDESCRIPTOR pfd = {
1329 sizeof(PIXELFORMATDESCRIPTOR),
1331 PFD_DRAW_TO_WINDOW |
1332 PFD_SUPPORT_OPENGL |
1337 0, 0, 0, 0, 0, 0, 0, 0,
1345 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1348 int format = ChoosePixelFormat(dc, &pfd);
1349 if (format == 0)
return "No suitable pixel format found";
1350 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1352 return std::nullopt;
1356static void LoadWGLExtensions()
1363 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1364 HDC dc = GetDC(wnd);
1367 if (SelectPixelFormat(dc) == std::nullopt) {
1369 HGLRC rc = wglCreateContext(dc);
1370 if (rc !=
nullptr) {
1371 wglMakeCurrent(dc, rc);
1375#pragma GCC diagnostic push
1376#pragma GCC diagnostic ignored "-Wcast-function-type"
1380 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1381 if (wglGetExtensionsStringARB !=
nullptr) {
1382 const char *wgl_exts = wglGetExtensionsStringARB(dc);
1384 if (FindStringInExtensionList(wgl_exts,
"WGL_ARB_create_context") !=
nullptr) {
1385 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1387 _hasWGLARBCreateContextProfile = FindStringInExtensionList(wgl_exts,
"WGL_ARB_create_context_profile") !=
nullptr;
1388 if (FindStringInExtensionList(wgl_exts,
"WGL_EXT_swap_control") !=
nullptr) {
1389 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1394#pragma GCC diagnostic pop
1396 wglMakeCurrent(
nullptr,
nullptr);
1397 wglDeleteContext(rc);
1405static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1407std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1413 LoadWGLExtensions();
1416 this->MakeWindow(_fullscreen);
1419 auto err = this->AllocateContext();
1426 this->driver_info = GetName();
1427 this->driver_info +=
" (";
1429 this->driver_info +=
")";
1431 this->ClientSizeChanged(this->width, this->height,
true);
1433 if (_screen.dst_ptr ==
nullptr) {
1436 return "Can't get pointer to screen buffer";
1439 this->ReleaseVideoPointer();
1445 return std::nullopt;
1448void VideoDriver_Win32OpenGL::Stop()
1450 this->DestroyContext();
1454void VideoDriver_Win32OpenGL::DestroyContext()
1458 wglMakeCurrent(
nullptr,
nullptr);
1459 if (this->gl_rc !=
nullptr) {
1460 wglDeleteContext(this->gl_rc);
1461 this->gl_rc =
nullptr;
1463 if (this->dc !=
nullptr) {
1464 ReleaseDC(this->main_wnd, this->dc);
1469void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1471 if (_wglSwapIntervalEXT !=
nullptr) {
1472 _wglSwapIntervalEXT(vsync);
1474 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1478std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1480 this->dc = GetDC(this->main_wnd);
1482 auto err = SelectPixelFormat(this->dc);
1483 if (err)
return err;
1488 if (_wglCreateContextAttribsARB !=
nullptr) {
1491 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1492 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1493 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1494 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1497 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1499 if (rc ==
nullptr) {
1503 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1507 if (rc ==
nullptr) {
1509 rc = wglCreateContext(this->dc);
1510 if (rc ==
nullptr)
return "Can't create OpenGL context";
1512 if (!wglMakeCurrent(this->dc, rc))
return "Can't active GL context";
1520bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1522 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1523 this->DestroyContext();
1525 res &= this->AllocateContext() == std::nullopt;
1526 this->ClientSizeChanged(this->width, this->height,
true);
1530bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1533 this->ClientSizeChanged(this->width, this->height,
true);
1537void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1542void VideoDriver_Win32OpenGL::ClearSystemSprites()
1547bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1549 if (!force && w == _screen.width && h == _screen.height)
return false;
1551 this->width = w = std::max(w, 64);
1552 this->height = h = std::max(h, 64);
1554 if (this->gl_rc ==
nullptr)
return false;
1556 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1558 this->dirty_rect = {};
1560 SwapBuffers(this->dc);
1561 _screen.dst_ptr = this->GetVideoPointer();
1566void *VideoDriver_Win32OpenGL::GetVideoPointer()
1574void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1578 this->dirty_rect = {};
1579 _screen.dst_ptr =
nullptr;
1580 this->anim_buffer =
nullptr;
1583void VideoDriver_Win32OpenGL::Paint()
1602 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.
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 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::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: