30#include <versionhelpers.h>
31#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
32#include <winrt/Windows.UI.ViewManagement.h>
37#include "../3rdparty/opengl/glext.h"
38#include "../3rdparty/opengl/wglext.h"
45#ifndef MAPVK_VK_TO_CHAR
46#define MAPVK_VK_TO_CHAR (2)
50#define PM_QS_INPUT 0x20000
54#define WM_DPICHANGED 0x02E0
65 MyShowCursor(
false,
true);
74#define AS(x, z) {x, 1, z}
75#define AM(x, y, z, w) {x, y - x + 1, z}
79 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
81 AM(
'A',
'Z',
'A',
'Z'),
82 AM(
'0',
'9',
'0',
'9'),
84 AS(VK_ESCAPE, WKC_ESC),
85 AS(VK_PAUSE, WKC_PAUSE),
86 AS(VK_BACK, WKC_BACKSPACE),
87 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
89 AS(VK_SPACE, WKC_SPACE),
90 AS(VK_RETURN, WKC_RETURN),
94 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
97 AM(VK_NUMPAD0, VK_NUMPAD9,
'0',
'9'),
98 AS(VK_DIVIDE, WKC_NUM_DIV),
99 AS(VK_MULTIPLY, WKC_NUM_MUL),
100 AS(VK_SUBTRACT, WKC_NUM_MINUS),
101 AS(VK_ADD, WKC_NUM_PLUS),
102 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
118static uint MapWindowsKey(uint sym)
122 for (
const auto &map : _vk_mapping) {
123 if (
IsInsideBS(sym, map.vk_from, map.vk_count)) {
124 key = sym - map.vk_from + map.map_to;
129 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
130 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
131 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
152 _fullscreen = full_screen;
172 if (
settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
177 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
179 GetWindowRect(GetDesktopWindow(), &r);
182 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
187 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
193 ChangeDisplaySettings(
nullptr, 0);
195 this->
width = _bck_resolution.width;
196 this->
height = _bck_resolution.height;
201 DWORD style, showstyle;
204 showstyle = SW_SHOWNORMAL;
210 style = WS_OVERLAPPEDWINDOW;
212 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
216 AdjustWindowRect(&r, style, FALSE);
217 w = r.right - r.left;
218 h = r.bottom - r.top;
221 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
229 mi.cbSize =
sizeof(mi);
230 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
232 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
233 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
237 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
238 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
239 ShowWindow(this->
main_wnd, showstyle);
252 static char32_t prev_char = 0;
256 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
257 prev_char = charcode;
262 if (prev_char != 0) {
266 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
279 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
285 HIMC hIMC = ImmGetContext(hwnd);
286 if (hIMC !=
nullptr) {
288 cf.dwStyle = CFS_POINT;
292 Point pt = _focused_window->GetCaretPosition();
293 cf.ptCurrentPos.x = _focused_window->left + pt.
x;
294 cf.ptCurrentPos.y = _focused_window->top + pt.
y;
296 cf.ptCurrentPos.x = 0;
297 cf.ptCurrentPos.y = 0;
299 ImmSetCompositionWindow(hIMC, &cf);
301 ImmReleaseContext(hwnd, hIMC);
307 HIMC hIMC = ImmGetContext(hwnd);
308 if (hIMC !=
nullptr) {
311 cf.dwStyle = CFS_EXCLUDE;
314 Point pt = _focused_window->GetCaretPosition();
315 cf.ptCurrentPos.x = _focused_window->left + pt.
x;
316 cf.ptCurrentPos.y = _focused_window->top + pt.
y;
317 if (_focused_window->window_class ==
WC_CONSOLE) {
318 cf.rcArea.left = _focused_window->left;
319 cf.rcArea.top = _focused_window->top;
320 cf.rcArea.right = _focused_window->left + _focused_window->width;
321 cf.rcArea.bottom = _focused_window->top + _focused_window->height;
323 cf.rcArea.left = _focused_window->left + _focused_window->nested_focus->pos_x;
324 cf.rcArea.top = _focused_window->top + _focused_window->nested_focus->pos_y;
325 cf.rcArea.right = cf.rcArea.left + _focused_window->nested_focus->current_x;
326 cf.rcArea.bottom = cf.rcArea.top + _focused_window->nested_focus->current_y;
329 cf.ptCurrentPos.x = 0;
330 cf.ptCurrentPos.y = 0;
331 SetRectEmpty(&cf.rcArea);
333 ImmSetCandidateWindow(hIMC, &cf);
335 ImmReleaseContext(hwnd, hIMC);
341 HIMC hIMC = ImmGetContext(hwnd);
343 if (hIMC !=
nullptr) {
344 if (lParam & GCS_RESULTSTR) {
346 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
347 std::wstring str(len + 1, L
'\0');
348 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
349 str[len /
sizeof(wchar_t)] = L
'\0';
359 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
364 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
365 std::wstring str(len + 1, L
'\0');
366 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
367 str[len /
sizeof(wchar_t)] = L
'\0';
370 static char utf8_buf[1024];
374 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
376 auto caret = view.begin();
377 const auto end = view.end();
378 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && caret != end && caret_bytes > 0; c++, caret_bytes--) {
392 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
395 ImmReleaseContext(hwnd, hIMC);
397 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
403 HIMC hIMC = ImmGetContext(hwnd);
404 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
405 ImmReleaseContext(hwnd, hIMC);
410#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
418extern "C" int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(int32_t error,
void *message,
void *languageException)
noexcept
420 typedef BOOL(WINAPI *PFNRoOriginateLanguageException)(int32_t,
void *,
void *);
421 static PFNRoOriginateLanguageException RoOriginateLanguageException = _combase.GetFunction(
"RoOriginateLanguageException");
423 if (RoOriginateLanguageException !=
nullptr) {
424 return RoOriginateLanguageException(error, message, languageException);
430extern "C" int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(
void *classId, winrt::guid
const &iid,
void **factory)
noexcept
432 typedef BOOL(WINAPI *PFNRoGetActivationFactory)(
void *, winrt::guid
const &,
void **);
433 static PFNRoGetActivationFactory RoGetActivationFactory = _combase.GetFunction(
"RoGetActivationFactory");
435 if (RoGetActivationFactory !=
nullptr) {
436 return RoGetActivationFactory(classId, iid, factory);
439 return winrt::impl::error_class_not_available;
444static bool IsDarkModeEnabled()
447#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
448 if (IsWindows10OrGreater()) {
458 winrt::Windows::UI::ViewManagement::UISettings
settings;
459 auto foreground =
settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
462 return ((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128);
473static void SetDarkModeForWindow(HWND hWnd,
bool dark_mode)
476#if defined(NTDDI_WIN10)
477 if (!IsWindows10OrGreater())
return;
482 typedef HRESULT(WINAPI *PFNDWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD);
483 static const PFNDWMSETWINDOWATTRIBUTE DwmSetWindowAttribute = _dwmapi.GetFunction(
"DwmSetWindowAttribute");
485 if (DwmSetWindowAttribute !=
nullptr) {
489 BOOL value = dark_mode ? TRUE : FALSE;
490 if (DwmSetWindowAttribute(hWnd, 20 , &value,
sizeof(value)) != S_OK) {
491 DwmSetWindowAttribute(hWnd, 19 , &value,
sizeof(value));
497LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
499 static uint32_t keycode = 0;
500 static bool console =
false;
502 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f / WHEEL_DELTA;
508 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
509 _cursor.in_window =
false;
514 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
517 case WM_SETTINGCHANGE:
519 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
524 GetUpdateRect(hwnd, &r, FALSE);
525 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
527 ValidateRect(hwnd,
nullptr);
531 case WM_PALETTECHANGED:
532 if ((HWND)wParam == hwnd)
return 0;
535 case WM_QUERYNEWPALETTE:
540 HandleExitGameRequest();
575 _cursor.in_window =
false;
581 int x = (int16_t)LOWORD(lParam);
582 int y = (int16_t)HIWORD(lParam);
587 if (!_cursor.in_window) {
588 _cursor.in_window =
true;
590 tme.cbSize =
sizeof(tme);
591 tme.dwFlags = TME_LEAVE;
592 tme.hwndTrack = hwnd;
594 TrackMouseEvent(&tme);
597 if (_cursor.fix_at) {
601 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
602 x = (int16_t)LOWORD(m.lParam);
603 y = (int16_t)HIWORD(m.lParam);
607 if (_cursor.UpdateCursorPosition(x, y)) {
609 pt.x = _cursor.pos.x;
610 pt.y = _cursor.pos.y;
611 ClientToScreen(hwnd, &pt);
612 SetCursorPos(pt.x, pt.y);
619 case WM_INPUTLANGCHANGE:
623 case WM_IME_SETCONTEXT:
628 case WM_IME_STARTCOMPOSITION:
633 case WM_IME_COMPOSITION:
636 case WM_IME_ENDCOMPOSITION:
647 console =
GB(lParam, 16, 8) == 41;
651 uint scancode =
GB(lParam, 16, 8);
652 uint charcode = wParam;
656 if (console && scancode == 41) {
663 uint cur_keycode = keycode;
671 uint scancode =
GB(lParam, 16, 8);
672 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
674 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
685 if (
HasBit(charcode, 31) && !console) {
686 if (scancode == 41) {
695 uint cur_keycode = keycode;
725 if (wParam != SIZE_MINIMIZED) {
728 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
729 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
730 video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
735 RECT *r = (RECT*)lParam;
739 SetRect(&r2, 0, 0, 0, 0);
740 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
742 w = r->right - r->left - (r2.right - r2.left);
743 h = r->bottom - r->top - (r2.bottom - r2.top);
746 SetRect(&r2, 0, 0, w, h);
748 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
749 w = r2.right - r2.left;
750 h = r2.bottom - r2.top;
754 r->bottom = r->top + h;
757 case WMSZ_BOTTOMLEFT:
758 r->bottom = r->top + h;
759 r->left = r->right - w;
762 case WMSZ_BOTTOMRIGHT:
763 r->bottom = r->top + h;
764 r->right = r->left + w;
768 r->left = r->right - w;
772 r->right = r->left + w;
776 r->top = r->bottom - h;
780 r->top = r->bottom - h;
781 r->left = r->right - w;
785 r->top = r->bottom - h;
786 r->right = r->left + w;
792 case WM_DPICHANGED: {
796 RECT *prcNewWindow = (RECT *)lParam;
801 prcNewWindow->right - prcNewWindow->left,
802 prcNewWindow->bottom - prcNewWindow->top,
803 SWP_NOZORDER | SWP_NOACTIVATE);
811#if !defined(WM_MOUSEWHEEL)
812# define WM_MOUSEWHEEL 0x020A
814#if !defined(WM_MOUSEHWHEEL)
815# define WM_MOUSEHWHEEL 0x020E
817#if !defined(GET_WHEEL_DELTA_WPARAM)
818# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
821 case WM_MOUSEWHEEL: {
822 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
826 }
else if (delta > 0) {
830 _cursor.v_wheel -=
static_cast<float>(delta) * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
831 _cursor.wheel_moved =
true;
836 case WM_MOUSEHWHEEL: {
837 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
839 _cursor.h_wheel +=
static_cast<float>(delta) * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
840 _cursor.wheel_moved =
true;
856 if (_exit_game)
break;
858 bool active = (LOWORD(wParam) != WA_INACTIVE);
859 bool minimized = (HIWORD(wParam) != 0);
861 if (active && minimized) {
864 ShowWindow(hwnd, SW_RESTORE);
867 }
else if (!active && !minimized) {
869 ShowWindow(hwnd, SW_MINIMIZE);
870 ChangeDisplaySettings(
nullptr, 0);
877 return DefWindowProc(hwnd, msg, wParam, lParam);
880static void RegisterWndClass()
882 static bool registered =
false;
884 if (registered)
return;
886 HINSTANCE hinst = GetModuleHandle(
nullptr);
893 LoadIcon(hinst, MAKEINTRESOURCE(100)),
894 LoadCursor(
nullptr, IDC_ARROW),
901 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
904static const Dimension default_resolutions[] = {
918static void FindResolutions(uint8_t bpp)
923 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
924 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
926 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
931 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
937void VideoDriver_Win32Base::Initialize()
955 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
967 this->
MakeDirty(0, 0, _screen.width, _screen.height);
984 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
985 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
986 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
987 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
999 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
1003 DispatchMessage(&mesg);
1013 if (_exit_game)
break;
1022void VideoDriver_Win32Base::ClientSizeChanged(
int w,
int h,
bool force)
1036 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
1059static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
1061 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
1063 MONITORINFOEX monitorInfo = {};
1064 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
1065 GetMonitorInfo(hMonitor, &monitorInfo);
1067 DEVMODE devMode = {};
1068 devMode.dmSize =
sizeof(DEVMODE);
1069 devMode.dmDriverExtra = 0;
1070 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
1072 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
1078 std::vector<int> rates = {};
1079 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
1085 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
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()
1294#ifndef PFD_SUPPORT_COMPOSITION
1295# define PFD_SUPPORT_COMPOSITION 0x00008000
1298static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1299static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1300static bool _hasWGLARBCreateContextProfile =
false;
1305 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1306 if (ret ==
nullptr) {
1308 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1318static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1320 PIXELFORMATDESCRIPTOR pfd = {
1321 sizeof(PIXELFORMATDESCRIPTOR),
1323 PFD_DRAW_TO_WINDOW |
1324 PFD_SUPPORT_OPENGL |
1329 0, 0, 0, 0, 0, 0, 0, 0,
1337 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1340 int format = ChoosePixelFormat(dc, &pfd);
1341 if (format == 0)
return "No suitable pixel format found";
1342 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1344 return std::nullopt;
1348static void LoadWGLExtensions()
1355 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1356 HDC dc = GetDC(wnd);
1359 if (SelectPixelFormat(dc) == std::nullopt) {
1361 HGLRC rc = wglCreateContext(dc);
1362 if (rc !=
nullptr) {
1363 wglMakeCurrent(dc, rc);
1367#pragma GCC diagnostic push
1368#pragma GCC diagnostic ignored "-Wcast-function-type"
1372 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1373 if (wglGetExtensionsStringARB !=
nullptr) {
1374 std::string_view wgl_exts = wglGetExtensionsStringARB(dc);
1377 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1381 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1386#pragma GCC diagnostic pop
1388 wglMakeCurrent(
nullptr,
nullptr);
1389 wglDeleteContext(rc);
1397static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1399std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1405 LoadWGLExtensions();
1408 this->MakeWindow(_fullscreen);
1411 auto err = this->AllocateContext();
1418 this->driver_info = GetName();
1419 this->driver_info +=
" (";
1421 this->driver_info +=
")";
1423 this->ClientSizeChanged(this->width, this->height,
true);
1425 if (_screen.dst_ptr ==
nullptr) {
1428 return "Can't get pointer to screen buffer";
1431 this->ReleaseVideoPointer();
1437 return std::nullopt;
1440void VideoDriver_Win32OpenGL::Stop()
1442 this->DestroyContext();
1446void VideoDriver_Win32OpenGL::DestroyContext()
1450 wglMakeCurrent(
nullptr,
nullptr);
1451 if (this->gl_rc !=
nullptr) {
1452 wglDeleteContext(this->gl_rc);
1453 this->gl_rc =
nullptr;
1455 if (this->dc !=
nullptr) {
1456 ReleaseDC(this->main_wnd, this->dc);
1461void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1463 if (_wglSwapIntervalEXT !=
nullptr) {
1464 _wglSwapIntervalEXT(vsync);
1466 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1470std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1472 this->dc = GetDC(this->main_wnd);
1474 auto err = SelectPixelFormat(this->dc);
1475 if (err)
return err;
1480 if (_wglCreateContextAttribsARB !=
nullptr) {
1483 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1484 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1485 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1486 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1489 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1491 if (rc ==
nullptr) {
1495 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1499 if (rc ==
nullptr) {
1501 rc = wglCreateContext(this->dc);
1502 if (rc ==
nullptr)
return "Can't create OpenGL context";
1504 if (!wglMakeCurrent(this->dc, rc))
return "Can't activate GL context";
1512bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1514 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1515 this->DestroyContext();
1517 res &= this->AllocateContext() == std::nullopt;
1518 this->ClientSizeChanged(this->width, this->height,
true);
1522bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1525 this->ClientSizeChanged(this->width, this->height,
true);
1529void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1534void VideoDriver_Win32OpenGL::ClearSystemSprites()
1539bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1541 if (!force && w == _screen.width && h == _screen.height)
return false;
1543 this->width = w = std::max(w, 64);
1544 this->height = h = std::max(h, 64);
1546 if (this->gl_rc ==
nullptr)
return false;
1548 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1550 this->dirty_rect = {};
1552 SwapBuffers(this->dc);
1553 _screen.dst_ptr = this->GetVideoPointer();
1558void *VideoDriver_Win32OpenGL::GetVideoPointer()
1566void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1570 this->dirty_rect = {};
1571 _screen.dst_ptr =
nullptr;
1572 this->anim_buffer =
nullptr;
1575void VideoDriver_Win32OpenGL::Paint()
1594 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.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
How all blitters should look like.
virtual uint8_t GetScreenDepth()=0
Get the screen depth this blitter works for.
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
@ None
No palette animation.
@ Blitter
The blitter takes care of the palette animation.
@ VideoBackend
Palette animation should be done by video backend (8bpp only!).
virtual void PostResize()
Post resize event.
The factory for Windows' video driver.
void Paint()
Render video buffer to the screen.
uint8_t * GetAnimBuffer()
Get a pointer to the memory for the separate animation buffer.
void * GetVideoBuffer()
Get a pointer to the memory for the video driver to draw to.
bool Resize(int w, int h, bool force=false)
Change the size of the drawing window and allocate matching resources.
static std::optional< std::string_view > Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res)
Create and initialize the singleton back-end class.
void UpdatePalette(const Colour *pal, uint first, uint length)
Update the stored palette.
void ReleaseAnimBuffer(const Rect &update_rect)
Update animation buffer texture after the animation buffer was filled.
void ClearCursorCache()
Queue a request for cursor cache clear.
static OpenGLBackend * Get()
Get singleton instance of this class.
void DrawMouseCursor()
Draw mouse cursor on screen.
void ReleaseVideoBuffer(const Rect &update_rect)
Update video buffer texture after the video buffer was filled.
static void Destroy()
Free resources and destroy singleton back-end class.
Constant span of UTF-8 encoded data.
Base class for Windows video drivers.
int height
Height in pixels of our display surface.
bool has_focus
Does our window have system focus?
void EditBoxLostFocus() override
An edit box lost the input focus.
void CheckPaletteAnim() override
Process any pending palette animation.
void Stop() override
Stop this driver.
int height_org
Original monitor resolution height, before we changed it.
HWND main_wnd
Handle to system window.
bool fullscreen
Whether to use (true) fullscreen mode.
int width_org
Original monitor resolution width, before we changed it.
bool MakeWindow(bool full_screen, bool resize=true)
Instantiate a new window.
bool buffer_locked
Video buffer was locked by the main thread.
virtual void * GetVideoPointer()=0
Get a pointer to the video buffer.
void MakeDirty(int left, int top, int width, int height) override
Mark a particular area dirty.
bool PollEvent() override
Process a single system event.
virtual void PaletteChanged(HWND hWnd)=0
Palette of the window has changed.
bool LockVideoBuffer() override
Make sure the video buffer is ready for drawing.
std::vector< int > GetListOfMonitorRefreshRates() override
Get a list of refresh rates of each available monitor.
virtual bool AllocateBackingStore(int w, int h, bool force=false)=0
(Re-)create the backing store.
int width
Width in pixels of our display surface.
void InputLoop() override
Handle input logic, is CTRL pressed, should we fast-forward, etc.
virtual uint8_t GetFullscreenBpp()
Get screen depth to use for fullscreen mode.
Dimension GetScreenSize() const override
Get the resolution of the main screen.
virtual void ReleaseVideoPointer()
Hand video buffer back to the painting backend.
void UnlockVideoBuffer() override
Unlock a previously locked video buffer.
void MainLoop() override
Perform the actual drawing.
void ClaimMousePointer() override
Claim the exclusive rights for the mouse pointer.
Rect dirty_rect
Region of the screen that needs redrawing.
bool ToggleFullscreen(bool fullscreen) override
Change the full screen setting.
bool ChangeResolution(int w, int h) override
Change the resolution of the window.
The GDI video driver for windows.
void * buffer_bits
Internal rendering buffer.
HBITMAP dib_sect
System bitmap object referencing our rendering buffer.
void PaletteChanged(HWND hWnd) override
Palette of the window has changed.
std::optional< std::string_view > Start(const StringList ¶m) override
Start this driver.
void * GetVideoPointer() override
Get a pointer to the video buffer.
HPALETTE gdi_palette
Palette object for 8bpp blitter.
bool AllocateBackingStore(int w, int h, bool force=false) override
(Re-)create the backing store.
void Paint() override
Paint the window.
bool AfterBlitterChange() override
Callback invoked after the blitter was changed.
void Stop() override
Stop this driver.
bool fast_forward_key_pressed
The fast-forward key is being pressed.
void Tick()
Give the video-driver a tick.
void SleepTillNextTick()
Sleep till the next tick is about to happen.
void StartGameThread()
Start the loop for game-tick.
static std::string GetCaption()
Get the caption to use for the game's title bar.
void StopGameThread()
Stop the loop for the game-tick.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
void UpdateAutoResolution()
Apply resolution auto-detection and clamp to sensible defaults.
static Palette _local_palette
Current palette to use for drawing.
static OGLProc GetOGLProcAddressCallback(const char *proc)
Platform-specific callback to get an OpenGL function pointer.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
bool GetDriverParamBool(const StringList &parm, std::string_view name)
Get a boolean parameter the list of parameters.
std::vector< Dimension > _resolutions
List of resolutions.
Dimension _cur_resolution
The current resolution.
Error reporting related functions.
Factory to 'query' all available blitters.
fluid_settings_t * settings
FluidSynth settings handle.
Types for recording game performance data.
@ 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.
Functions related to the gfx engine.
void HandleCtrlChanged()
State of CONTROL key has changed.
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
void GameSizeChanged()
Size of the application screen changed.
void HandleMouseEvents()
Handle a mouse event from the video driver.
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
void HandleTextInput(std::string_view str, bool marked=false, std::optional< size_t > caret=std::nullopt, std::optional< size_t > insert_location=std::nullopt, std::optional< size_t > replacement_end=std::nullopt)
Handle text input.
@ S8BPP_HARDWARE
Full 8bpp support by OS and hardware.
@ WKC_BACKSLASH
\ Backslash
@ WKC_SLASH
/ Forward slash
@ WKC_SINGLEQUOTE
' Single quote
@ WKC_R_BRACKET
] Right square bracket
@ WKC_L_BRACKET
[ Left square bracket
@ WKC_SEMICOLON
; Semicolon
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Functions/types related to loading libraries dynamically.
#define Point
Macro that prevents name conflicts between included headers.
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.
bool HasStringInExtensionList(std::string_view string, std::string_view substring)
Find a substring in a string made of space delimited elements.
OpenGL video driver support.
@ Stop
Go to the depot and stop there.
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.
Functions related to modal progress.
Pseudo random number generator.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition of base types and functions in a cross-platform compatible way.
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.
Dimensions (a width and height) of a rectangle in 2D.
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Specification of a rectangle with absolute coordinates of all edges.
Functions related to text effects.
Handling of UTF-8 encoded data.
bool _video_vsync
Whether we should use vsync (only if active video driver supports HW acceleration).
std::string_view convert_from_fs(const std::wstring_view src, std::span< char > dst_buf)
Convert to OpenTTD's encoding from that of the environment in UNICODE.
std::wstring OTTD2FS(std::string_view name)
Convert from OpenTTD's encoding to a wide string.
std::string FS2OTTD(std::wstring_view name)
Convert to OpenTTD's encoding from a wide string.
Declarations of functions for MS windows systems.
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...
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ WC_CONSOLE
Console; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers: