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>
31#include "../safeguards.h"
34#ifndef MAPVK_VK_TO_CHAR
35#define MAPVK_VK_TO_CHAR (2)
39#define PM_QS_INPUT 0x20000
43#define WM_DPICHANGED 0x02E0
52bool VideoDriver_Win32Base::ClaimMousePointer()
54 MyShowCursor(
false,
true);
64#define AS(x, z) {x, 1, z}
65#define AM(x, y, z, w) {x, y - x + 1, z}
69 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
71 AM(
'A',
'Z',
'A',
'Z'),
72 AM(
'0',
'9',
'0',
'9'),
74 AS(VK_ESCAPE, WKC_ESC),
75 AS(VK_PAUSE, WKC_PAUSE),
76 AS(VK_BACK, WKC_BACKSPACE),
77 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
79 AS(VK_SPACE, WKC_SPACE),
80 AS(VK_RETURN, WKC_RETURN),
84 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
87 AM(VK_NUMPAD0, VK_NUMPAD9,
'0',
'9'),
88 AS(VK_DIVIDE, WKC_NUM_DIV),
89 AS(VK_MULTIPLY, WKC_NUM_MUL),
90 AS(VK_SUBTRACT, WKC_NUM_MINUS),
91 AS(VK_ADD, WKC_NUM_PLUS),
92 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
108static uint MapWindowsKey(uint sym)
112 for (
const auto &map : _vk_mapping) {
113 if (
IsInsideBS(sym, map.vk_from, map.vk_count)) {
114 key = sym - map.vk_from + map.map_to;
119 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
120 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
121 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
142 _fullscreen = full_screen;
164 if (settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
169 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
171 GetWindowRect(GetDesktopWindow(), &r);
174 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
179 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
185 ChangeDisplaySettings(
nullptr, 0);
187 this->
width = _bck_resolution.width;
188 this->
height = _bck_resolution.height;
193 DWORD style, showstyle;
196 showstyle = SW_SHOWNORMAL;
202 style = WS_OVERLAPPEDWINDOW;
204 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
208 AdjustWindowRect(&r, style, FALSE);
209 w = r.right - r.left;
210 h = r.bottom - r.top;
213 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
221 mi.cbSize =
sizeof(mi);
222 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
224 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
225 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
229 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
230 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
231 ShowWindow(this->
main_wnd, showstyle);
244 static char32_t prev_char = 0;
248 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
249 prev_char = charcode;
254 if (prev_char != 0) {
258 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
271 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
277 HIMC hIMC = ImmGetContext(hwnd);
278 if (hIMC !=
nullptr) {
280 cf.dwStyle = CFS_POINT;
285 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
286 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
288 cf.ptCurrentPos.x = 0;
289 cf.ptCurrentPos.y = 0;
291 ImmSetCompositionWindow(hIMC, &cf);
293 ImmReleaseContext(hwnd, hIMC);
299 HIMC hIMC = ImmGetContext(hwnd);
300 if (hIMC !=
nullptr) {
303 cf.dwStyle = CFS_EXCLUDE;
307 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
308 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
310 cf.rcArea.left = _focused_window->
left;
311 cf.rcArea.top = _focused_window->
top;
312 cf.rcArea.right = _focused_window->
left + _focused_window->
width;
313 cf.rcArea.bottom = _focused_window->
top + _focused_window->
height;
321 cf.ptCurrentPos.x = 0;
322 cf.ptCurrentPos.y = 0;
323 SetRectEmpty(&cf.rcArea);
325 ImmSetCandidateWindow(hIMC, &cf);
327 ImmReleaseContext(hwnd, hIMC);
333 HIMC hIMC = ImmGetContext(hwnd);
335 if (hIMC !=
nullptr) {
336 if (lParam & GCS_RESULTSTR) {
338 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
339 std::wstring str(len + 1, L
'\0');
340 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
341 str[len /
sizeof(wchar_t)] = L
'\0';
351 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
356 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
357 std::wstring str(len + 1, L
'\0');
358 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
359 str[len /
sizeof(wchar_t)] = L
'\0';
362 static char utf8_buf[1024];
366 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
367 const char *caret = utf8_buf;
368 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && *caret !=
'\0' && caret_bytes > 0; c++, caret_bytes--) {
382 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
385 ImmReleaseContext(hwnd, hIMC);
387 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
393 HIMC hIMC = ImmGetContext(hwnd);
394 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
395 ImmReleaseContext(hwnd, hIMC);
400LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
402 static uint32_t keycode = 0;
403 static bool console =
false;
409 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
417 GetUpdateRect(hwnd, &r, FALSE);
418 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
420 ValidateRect(hwnd,
nullptr);
424 case WM_PALETTECHANGED:
425 if ((HWND)wParam == hwnd)
return 0;
428 case WM_QUERYNEWPALETTE:
433 HandleExitGameRequest();
474 int x = (int16_t)LOWORD(lParam);
475 int y = (int16_t)HIWORD(lParam);
483 tme.cbSize =
sizeof(tme);
484 tme.dwFlags = TME_LEAVE;
485 tme.hwndTrack = hwnd;
487 TrackMouseEvent(&tme);
494 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
495 x = (int16_t)LOWORD(m.lParam);
496 y = (int16_t)HIWORD(m.lParam);
502 pt.x = _cursor.
pos.x;
503 pt.y = _cursor.
pos.y;
504 ClientToScreen(hwnd, &pt);
505 SetCursorPos(pt.x, pt.y);
512 case WM_INPUTLANGCHANGE:
516 case WM_IME_SETCONTEXT:
521 case WM_IME_STARTCOMPOSITION:
526 case WM_IME_COMPOSITION:
529 case WM_IME_ENDCOMPOSITION:
540 console =
GB(lParam, 16, 8) == 41;
544 uint scancode =
GB(lParam, 16, 8);
545 uint charcode = wParam;
549 if (console && scancode == 41) {
556 uint cur_keycode = keycode;
564 uint scancode =
GB(lParam, 16, 8);
565 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
567 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
578 if (
HasBit(charcode, 31) && !console) {
579 if (scancode == 41) {
588 uint cur_keycode = keycode;
618 if (wParam != SIZE_MINIMIZED) {
621 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
622 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
623 video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
628 RECT *r = (RECT*)lParam;
632 SetRect(&r2, 0, 0, 0, 0);
633 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
635 w = r->right - r->left - (r2.right - r2.left);
636 h = r->bottom - r->top - (r2.bottom - r2.top);
639 SetRect(&r2, 0, 0, w, h);
641 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
642 w = r2.right - r2.left;
643 h = r2.bottom - r2.top;
647 r->bottom = r->top + h;
650 case WMSZ_BOTTOMLEFT:
651 r->bottom = r->top + h;
652 r->left = r->right - w;
655 case WMSZ_BOTTOMRIGHT:
656 r->bottom = r->top + h;
657 r->right = r->left + w;
661 r->left = r->right - w;
665 r->right = r->left + w;
669 r->top = r->bottom - h;
673 r->top = r->bottom - h;
674 r->left = r->right - w;
678 r->top = r->bottom - h;
679 r->right = r->left + w;
685 case WM_DPICHANGED: {
689 RECT *prcNewWindow = (RECT *)lParam;
694 prcNewWindow->right - prcNewWindow->left,
695 prcNewWindow->bottom - prcNewWindow->top,
696 SWP_NOZORDER | SWP_NOACTIVATE);
704#if !defined(WM_MOUSEWHEEL)
705# define WM_MOUSEWHEEL 0x020A
707#if !defined(GET_WHEEL_DELTA_WPARAM)
708# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
711 case WM_MOUSEWHEEL: {
712 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
716 }
else if (delta > 0) {
734 if (_exit_game)
break;
736 bool active = (LOWORD(wParam) != WA_INACTIVE);
737 bool minimized = (HIWORD(wParam) != 0);
739 if (active && minimized) {
742 ShowWindow(hwnd, SW_RESTORE);
745 }
else if (!active && !minimized) {
747 ShowWindow(hwnd, SW_MINIMIZE);
748 ChangeDisplaySettings(
nullptr, 0);
755 return DefWindowProc(hwnd, msg, wParam, lParam);
758static void RegisterWndClass()
760 static bool registered =
false;
762 if (registered)
return;
764 HINSTANCE hinst = GetModuleHandle(
nullptr);
771 LoadIcon(hinst, MAKEINTRESOURCE(100)),
772 LoadCursor(
nullptr, IDC_ARROW),
779 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
782static const Dimension default_resolutions[] = {
796static void FindResolutions(uint8_t bpp)
801 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
802 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
804 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
809 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
815void VideoDriver_Win32Base::Initialize()
833 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
845 this->
MakeDirty(0, 0, _screen.width, _screen.height);
862 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
863 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
864 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
865 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
877 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
881 DispatchMessage(&mesg);
891 if (_exit_game)
break;
900void VideoDriver_Win32Base::ClientSizeChanged(
int w,
int h,
bool force)
914 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
937static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
939 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
941 MONITORINFOEX monitorInfo = {};
942 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
943 GetMonitorInfo(hMonitor, &monitorInfo);
945 DEVMODE devMode = {};
946 devMode.dmSize =
sizeof(DEVMODE);
947 devMode.dmDriverExtra = 0;
948 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
950 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
956 std::vector<int> rates = {};
957 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
963 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
968 typedef UINT (WINAPI *PFNGETDPIFORWINDOW)(HWND hwnd);
969 typedef UINT (WINAPI *PFNGETDPIFORSYSTEM)(VOID);
970 typedef HRESULT (WINAPI *PFNGETDPIFORMONITOR)(HMONITOR hMonitor,
int dpiType, UINT *dpiX, UINT *dpiY);
972 static PFNGETDPIFORWINDOW _GetDpiForWindow =
nullptr;
973 static PFNGETDPIFORSYSTEM _GetDpiForSystem =
nullptr;
974 static PFNGETDPIFORMONITOR _GetDpiForMonitor =
nullptr;
976 static bool init_done =
false;
981 _GetDpiForWindow = _user32.
GetFunction(
"GetDpiForWindow");
982 _GetDpiForSystem = _user32.
GetFunction(
"GetDpiForSystem");
983 _GetDpiForMonitor = _shcore.
GetFunction(
"GetDpiForMonitor");
988 if (cur_dpi == 0 && _GetDpiForWindow !=
nullptr && this->
main_wnd !=
nullptr) {
990 cur_dpi = _GetDpiForWindow(this->
main_wnd);
992 if (cur_dpi == 0 && _GetDpiForMonitor !=
nullptr && this->
main_wnd !=
nullptr) {
995 if (SUCCEEDED(_GetDpiForMonitor(MonitorFromWindow(this->
main_wnd, MONITOR_DEFAULTTOPRIMARY), 0 , &dpiX, &dpiY))) {
999 if (cur_dpi == 0 && _GetDpiForSystem !=
nullptr) {
1001 cur_dpi = _GetDpiForSystem();
1004 return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f;
1013 assert(_screen.dst_ptr !=
nullptr);
1020 assert(_screen.dst_ptr !=
nullptr);
1021 if (_screen.dst_ptr !=
nullptr) {
1024 _screen.dst_ptr =
nullptr;
1039 this->MakePalette();
1047 return std::nullopt;
1062 w = std::max(w, 64);
1063 h = std::max(h, 64);
1065 if (!force && w == _screen.width && h == _screen.height)
return false;
1067 BITMAPINFO *bi = (BITMAPINFO *)
new char[
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256]();
1068 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1070 bi->bmiHeader.biWidth = this->
width = w;
1071 bi->bmiHeader.biHeight = -(this->
height = h);
1073 bi->bmiHeader.biPlanes = 1;
1074 bi->bmiHeader.biBitCount = bpp;
1075 bi->bmiHeader.biCompression = BI_RGB;
1080 this->
dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID **)&this->
buffer_bits,
nullptr, 0);
1083 UserError(
"CreateDIBSection failed");
1088 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1102void VideoDriver_Win32GDI::MakePalette()
1106 LOGPALETTE *pal = (LOGPALETTE *)
new char[
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY)]();
1108 pal->palVersion = 0x300;
1109 pal->palNumEntries = 256;
1111 for (uint i = 0; i != 256; i++) {
1115 pal->palPalEntry[i].peFlags = 0;
1120 if (this->
gdi_palette ==
nullptr) UserError(
"CreatePalette failed!\n");
1123void VideoDriver_Win32GDI::UpdatePalette(HDC dc, uint start, uint count)
1127 for (uint i = 0; i != count; i++) {
1131 rgb[i].rgbReserved = 0;
1134 SetDIBColorTable(dc, start, count, rgb);
1139 HDC hDC = GetWindowDC(hWnd);
1140 HPALETTE hOldPalette = SelectPalette(hDC, this->
gdi_palette, FALSE);
1141 UINT nChanged = RealizePalette(hDC);
1143 SelectPalette(hDC, hOldPalette, TRUE);
1144 ReleaseDC(hWnd, hDC);
1145 if (nChanged != 0) this->
MakeDirty(0, 0, _screen.width, _screen.height);
1155 HDC dc2 = CreateCompatibleDC(dc);
1157 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, this->
dib_sect);
1158 HPALETTE old_palette = SelectPalette(dc, this->
gdi_palette, FALSE);
1182 BitBlt(dc, 0, 0, this->
width, this->
height, dc2, 0, 0, SRCCOPY);
1183 SelectPalette(dc, old_palette, TRUE);
1184 SelectObject(dc2, old_bmp);
1195 int VideoDriver_Win32GDI::RedrawScreenDebug()
1214#include "../3rdparty/opengl/glext.h"
1215#include "../3rdparty/opengl/wglext.h"
1218#ifndef PFD_SUPPORT_COMPOSITION
1219# define PFD_SUPPORT_COMPOSITION 0x00008000
1222static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1223static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1224static bool _hasWGLARBCreateContextProfile =
false;
1229 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1230 if (ret ==
nullptr) {
1232 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1242static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1244 PIXELFORMATDESCRIPTOR pfd = {
1245 sizeof(PIXELFORMATDESCRIPTOR),
1247 PFD_DRAW_TO_WINDOW |
1248 PFD_SUPPORT_OPENGL |
1253 0, 0, 0, 0, 0, 0, 0, 0,
1261 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1264 int format = ChoosePixelFormat(dc, &pfd);
1265 if (format == 0)
return "No suitable pixel format found";
1266 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1268 return std::nullopt;
1272static void LoadWGLExtensions()
1279 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1280 HDC dc = GetDC(wnd);
1283 if (SelectPixelFormat(dc) == std::nullopt) {
1285 HGLRC rc = wglCreateContext(dc);
1286 if (rc !=
nullptr) {
1287 wglMakeCurrent(dc, rc);
1291#pragma GCC diagnostic push
1292#pragma GCC diagnostic ignored "-Wcast-function-type"
1296 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1297 if (wglGetExtensionsStringARB !=
nullptr) {
1298 const char *wgl_exts = wglGetExtensionsStringARB(dc);
1300 if (FindStringInExtensionList(wgl_exts,
"WGL_ARB_create_context") !=
nullptr) {
1301 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1303 _hasWGLARBCreateContextProfile = FindStringInExtensionList(wgl_exts,
"WGL_ARB_create_context_profile") !=
nullptr;
1304 if (FindStringInExtensionList(wgl_exts,
"WGL_EXT_swap_control") !=
nullptr) {
1305 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1310#pragma GCC diagnostic pop
1312 wglMakeCurrent(
nullptr,
nullptr);
1313 wglDeleteContext(rc);
1321static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1323std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1329 LoadWGLExtensions();
1332 this->MakeWindow(_fullscreen);
1335 auto err = this->AllocateContext();
1342 this->driver_info = GetName();
1343 this->driver_info +=
" (";
1345 this->driver_info +=
")";
1347 this->ClientSizeChanged(this->width, this->height,
true);
1349 if (_screen.dst_ptr ==
nullptr) {
1352 return "Can't get pointer to screen buffer";
1355 this->ReleaseVideoPointer();
1361 return std::nullopt;
1364void VideoDriver_Win32OpenGL::Stop()
1366 this->DestroyContext();
1370void VideoDriver_Win32OpenGL::DestroyContext()
1374 wglMakeCurrent(
nullptr,
nullptr);
1375 if (this->gl_rc !=
nullptr) {
1376 wglDeleteContext(this->gl_rc);
1377 this->gl_rc =
nullptr;
1379 if (this->dc !=
nullptr) {
1380 ReleaseDC(this->main_wnd, this->dc);
1385void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1387 if (_wglSwapIntervalEXT !=
nullptr) {
1388 _wglSwapIntervalEXT(vsync);
1390 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1394std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1396 this->dc = GetDC(this->main_wnd);
1398 auto err = SelectPixelFormat(this->dc);
1399 if (err)
return err;
1404 if (_wglCreateContextAttribsARB !=
nullptr) {
1407 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1408 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1409 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1410 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1413 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1415 if (rc ==
nullptr) {
1419 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1423 if (rc ==
nullptr) {
1425 rc = wglCreateContext(this->dc);
1426 if (rc ==
nullptr)
return "Can't create OpenGL context";
1428 if (!wglMakeCurrent(this->dc, rc))
return "Can't active GL context";
1436bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1438 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1439 this->DestroyContext();
1441 res &= this->AllocateContext() == std::nullopt;
1442 this->ClientSizeChanged(this->width, this->height,
true);
1446bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1449 this->ClientSizeChanged(this->width, this->height,
true);
1453void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1458void VideoDriver_Win32OpenGL::ClearSystemSprites()
1463bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1465 if (!force && w == _screen.width && h == _screen.height)
return false;
1467 this->width = w = std::max(w, 64);
1468 this->height = h = std::max(h, 64);
1470 if (this->gl_rc ==
nullptr)
return false;
1472 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1474 this->dirty_rect = {};
1476 SwapBuffers(this->dc);
1477 _screen.dst_ptr = this->GetVideoPointer();
1482void *VideoDriver_Win32OpenGL::GetVideoPointer()
1490void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1494 this->dirty_rect = {};
1495 _screen.dst_ptr =
nullptr;
1496 this->anim_buffer =
nullptr;
1499void VideoDriver_Win32OpenGL::Paint()
1518 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.
@ PALETTE_ANIMATION_NONE
No palette animation.
@ PALETTE_ANIMATION_VIDEO_BACKEND
Palette animation should be done by video backend (8bpp only!)
@ PALETTE_ANIMATION_BLITTER
The blitter takes care of the palette animation.
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...
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.
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.
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.
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)
uint8_t b
colour channels in BE order
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: