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;
155 _fullscreen = full_screen;
175 if (
settings.dmBitsPerPel == 8 && ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
180 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
182 GetWindowRect(GetDesktopWindow(), &r);
185 if ((
int)
settings.dmPelsWidth != r.right - r.left || (
int)
settings.dmPelsHeight != r.bottom - r.top) {
190 if (ChangeDisplaySettings(&
settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
196 ChangeDisplaySettings(
nullptr, 0);
198 this->
width = _bck_resolution.width;
199 this->
height = _bck_resolution.height;
204 DWORD style, showstyle;
207 showstyle = SW_SHOWNORMAL;
213 style = WS_OVERLAPPEDWINDOW;
215 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
219 AdjustWindowRect(&r, style, FALSE);
220 w = r.right - r.left;
221 h = r.bottom - r.top;
224 if (!_window_maximize && resize) SetWindowPos(this->
main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
232 mi.cbSize =
sizeof(mi);
233 GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi);
235 x = (mi.rcWork.right - mi.rcWork.left - w) / 2;
236 y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2;
240 this->
main_wnd = CreateWindow(L
"OTTD",
OTTD2FS(caption).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(
nullptr),
this);
241 if (this->
main_wnd ==
nullptr) UserError(
"CreateWindow failed");
242 ShowWindow(this->
main_wnd, showstyle);
255 static char32_t prev_char = 0;
259 if (prev_char != 0)
Debug(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
260 prev_char = charcode;
265 if (prev_char != 0) {
269 Debug(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
282 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
288 HIMC hIMC = ImmGetContext(hwnd);
289 if (hIMC !=
nullptr) {
291 cf.dwStyle = CFS_POINT;
295 Point pt = _focused_window->GetCaretPosition();
296 cf.ptCurrentPos.x = _focused_window->left + pt.
x;
297 cf.ptCurrentPos.y = _focused_window->top + pt.
y;
299 cf.ptCurrentPos.x = 0;
300 cf.ptCurrentPos.y = 0;
302 ImmSetCompositionWindow(hIMC, &cf);
304 ImmReleaseContext(hwnd, hIMC);
310 HIMC hIMC = ImmGetContext(hwnd);
311 if (hIMC !=
nullptr) {
314 cf.dwStyle = CFS_EXCLUDE;
317 Point pt = _focused_window->GetCaretPosition();
318 cf.ptCurrentPos.x = _focused_window->left + pt.
x;
319 cf.ptCurrentPos.y = _focused_window->top + pt.
y;
320 if (_focused_window->window_class ==
WC_CONSOLE) {
321 cf.rcArea.left = _focused_window->left;
322 cf.rcArea.top = _focused_window->top;
323 cf.rcArea.right = _focused_window->left + _focused_window->width;
324 cf.rcArea.bottom = _focused_window->top + _focused_window->height;
326 cf.rcArea.left = _focused_window->left + _focused_window->nested_focus->pos_x;
327 cf.rcArea.top = _focused_window->top + _focused_window->nested_focus->pos_y;
328 cf.rcArea.right = cf.rcArea.left + _focused_window->nested_focus->current_x;
329 cf.rcArea.bottom = cf.rcArea.top + _focused_window->nested_focus->current_y;
332 cf.ptCurrentPos.x = 0;
333 cf.ptCurrentPos.y = 0;
334 SetRectEmpty(&cf.rcArea);
336 ImmSetCandidateWindow(hIMC, &cf);
338 ImmReleaseContext(hwnd, hIMC);
344 HIMC hIMC = ImmGetContext(hwnd);
346 if (hIMC !=
nullptr) {
347 if (lParam & GCS_RESULTSTR) {
349 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR,
nullptr, 0);
350 std::wstring str(len + 1, L
'\0');
351 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str.data(), len);
352 str[len /
sizeof(wchar_t)] = L
'\0';
362 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
367 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR,
nullptr, 0);
368 std::wstring str(len + 1, L
'\0');
369 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str.data(), len);
370 str[len /
sizeof(wchar_t)] = L
'\0';
373 static char utf8_buf[1024];
377 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS,
nullptr, 0);
379 auto caret = view.begin();
380 const auto end = view.end();
381 for (
const wchar_t *c = str.c_str(); *c !=
'\0' && caret != end && caret_bytes > 0; c++, caret_bytes--) {
395 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
398 ImmReleaseContext(hwnd, hIMC);
400 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
406 HIMC hIMC = ImmGetContext(hwnd);
407 if (hIMC !=
nullptr) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
408 ImmReleaseContext(hwnd, hIMC);
413#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
421extern "C" int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(int32_t error,
void *message,
void *languageException)
noexcept
423 typedef BOOL(WINAPI *PFNRoOriginateLanguageException)(int32_t,
void *,
void *);
424 static PFNRoOriginateLanguageException RoOriginateLanguageException = _combase.GetFunction(
"RoOriginateLanguageException");
426 if (RoOriginateLanguageException !=
nullptr) {
427 return RoOriginateLanguageException(error, message, languageException);
433extern "C" int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(
void *classId, winrt::guid
const &iid,
void **factory)
noexcept
435 typedef BOOL(WINAPI *PFNRoGetActivationFactory)(
void *, winrt::guid
const &,
void **);
436 static PFNRoGetActivationFactory RoGetActivationFactory = _combase.GetFunction(
"RoGetActivationFactory");
438 if (RoGetActivationFactory !=
nullptr) {
439 return RoGetActivationFactory(classId, iid, factory);
442 return winrt::impl::error_class_not_available;
447static bool IsDarkModeEnabled()
450#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
451 if (IsWindows10OrGreater()) {
461 winrt::Windows::UI::ViewManagement::UISettings
settings;
462 auto foreground =
settings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Foreground);
465 return ((5 * foreground.G) + (2 * foreground.R) + foreground.B) > (8 * 128);
476static void SetDarkModeForWindow(HWND hWnd,
bool dark_mode)
479#if defined(NTDDI_WIN10)
480 if (!IsWindows10OrGreater())
return;
485 typedef HRESULT(WINAPI *PFNDWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD);
486 static const PFNDWMSETWINDOWATTRIBUTE DwmSetWindowAttribute = _dwmapi.GetFunction(
"DwmSetWindowAttribute");
488 if (DwmSetWindowAttribute !=
nullptr) {
492 BOOL value = dark_mode ? TRUE : FALSE;
493 if (DwmSetWindowAttribute(hWnd, 20 , &value,
sizeof(value)) != S_OK) {
494 DwmSetWindowAttribute(hWnd, 19 , &value,
sizeof(value));
500LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
502 static uint32_t keycode = 0;
503 static bool console =
false;
505 const float SCROLL_BUILTIN_MULTIPLIER = 14.0f / WHEEL_DELTA;
511 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams);
512 _cursor.in_window =
false;
517 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
520 case WM_SETTINGCHANGE:
522 SetDarkModeForWindow(hwnd, IsDarkModeEnabled());
527 GetUpdateRect(hwnd, &r, FALSE);
528 video_driver->
MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
530 ValidateRect(hwnd,
nullptr);
534 case WM_PALETTECHANGED:
535 if ((HWND)wParam == hwnd)
return 0;
538 case WM_QUERYNEWPALETTE:
543 HandleExitGameRequest();
578 _cursor.in_window =
false;
584 int x = (int16_t)LOWORD(lParam);
585 int y = (int16_t)HIWORD(lParam);
590 if (!_cursor.in_window) {
591 _cursor.in_window =
true;
593 tme.cbSize =
sizeof(tme);
594 tme.dwFlags = TME_LEAVE;
595 tme.hwndTrack = hwnd;
597 TrackMouseEvent(&tme);
600 if (_cursor.fix_at) {
604 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
605 x = (int16_t)LOWORD(m.lParam);
606 y = (int16_t)HIWORD(m.lParam);
610 if (_cursor.UpdateCursorPosition(x, y)) {
612 pt.x = _cursor.pos.x;
613 pt.y = _cursor.pos.y;
614 ClientToScreen(hwnd, &pt);
615 SetCursorPos(pt.x, pt.y);
622 case WM_INPUTLANGCHANGE:
626 case WM_IME_SETCONTEXT:
631 case WM_IME_STARTCOMPOSITION:
636 case WM_IME_COMPOSITION:
639 case WM_IME_ENDCOMPOSITION:
650 console =
GB(lParam, 16, 8) == 41;
654 uint scancode =
GB(lParam, 16, 8);
655 uint charcode = wParam;
659 if (console && scancode == 41) {
666 uint cur_keycode = keycode;
674 uint scancode =
GB(lParam, 16, 8);
675 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
677 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
688 if (
HasBit(charcode, 31) && !console) {
689 if (scancode == 41) {
698 uint cur_keycode = keycode;
728 if (wParam != SIZE_MINIMIZED) {
731 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
732 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
733 video_driver->ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
738 RECT *r = (RECT*)lParam;
742 SetRect(&r2, 0, 0, 0, 0);
743 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
745 w = r->right - r->left - (r2.right - r2.left);
746 h = r->bottom - r->top - (r2.bottom - r2.top);
749 SetRect(&r2, 0, 0, w, h);
751 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
752 w = r2.right - r2.left;
753 h = r2.bottom - r2.top;
757 r->bottom = r->top + h;
760 case WMSZ_BOTTOMLEFT:
761 r->bottom = r->top + h;
762 r->left = r->right - w;
765 case WMSZ_BOTTOMRIGHT:
766 r->bottom = r->top + h;
767 r->right = r->left + w;
771 r->left = r->right - w;
775 r->right = r->left + w;
779 r->top = r->bottom - h;
783 r->top = r->bottom - h;
784 r->left = r->right - w;
788 r->top = r->bottom - h;
789 r->right = r->left + w;
795 case WM_DPICHANGED: {
799 RECT *prcNewWindow = (RECT *)lParam;
804 prcNewWindow->right - prcNewWindow->left,
805 prcNewWindow->bottom - prcNewWindow->top,
806 SWP_NOZORDER | SWP_NOACTIVATE);
814#if !defined(WM_MOUSEWHEEL)
815# define WM_MOUSEWHEEL 0x020A
817#if !defined(WM_MOUSEHWHEEL)
818# define WM_MOUSEHWHEEL 0x020E
820#if !defined(GET_WHEEL_DELTA_WPARAM)
821# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
824 case WM_MOUSEWHEEL: {
825 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
829 }
else if (delta > 0) {
833 _cursor.v_wheel -=
static_cast<float>(delta) * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
834 _cursor.wheel_moved =
true;
839 case WM_MOUSEHWHEEL: {
840 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
842 _cursor.h_wheel +=
static_cast<float>(delta) * SCROLL_BUILTIN_MULTIPLIER *
_settings_client.gui.scrollwheel_multiplier;
843 _cursor.wheel_moved =
true;
859 if (_exit_game)
break;
861 bool active = (LOWORD(wParam) != WA_INACTIVE);
862 bool minimized = (HIWORD(wParam) != 0);
864 if (active && minimized) {
867 ShowWindow(hwnd, SW_RESTORE);
870 }
else if (!active && !minimized) {
872 ShowWindow(hwnd, SW_MINIMIZE);
873 ChangeDisplaySettings(
nullptr, 0);
880 return DefWindowProc(hwnd, msg, wParam, lParam);
883static void RegisterWndClass()
885 static bool registered =
false;
887 if (registered)
return;
889 HINSTANCE hinst = GetModuleHandle(
nullptr);
896 LoadIcon(hinst, MAKEINTRESOURCE(100)),
897 LoadCursor(
nullptr, IDC_ARROW),
904 if (!RegisterClass(&wnd)) UserError(
"RegisterClass failed");
907static const Dimension default_resolutions[] = {
921static void FindResolutions(uint8_t bpp)
926 for (uint i = 0; EnumDisplaySettings(
nullptr, i, &dm) != 0; i++) {
927 if (dm.dmBitsPerPel != bpp || dm.dmPelsWidth < 640 || dm.dmPelsHeight < 480)
continue;
929 _resolutions.emplace_back(dm.dmPelsWidth, dm.dmPelsHeight);
934 _resolutions.assign(std::begin(default_resolutions), std::end(default_resolutions));
940void VideoDriver_Win32Base::Initialize()
958 if (this->
fullscreen) ChangeDisplaySettings(
nullptr, 0);
970 this->
MakeDirty(0, 0, _screen.width, _screen.height);
987 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
988 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
989 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
990 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
1002 if (!PeekMessage(&mesg,
nullptr, 0, 0, PM_REMOVE))
return false;
1006 DispatchMessage(&mesg);
1016 if (_exit_game)
break;
1025void VideoDriver_Win32Base::ClientSizeChanged(
int w,
int h,
bool force)
1039 if (_window_maximize) ShowWindow(this->
main_wnd, SW_SHOWNORMAL);
1062static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM data)
1064 auto &list = *
reinterpret_cast<std::vector<int>*
>(data);
1066 MONITORINFOEX monitorInfo = {};
1067 monitorInfo.cbSize =
sizeof(MONITORINFOEX);
1068 GetMonitorInfo(hMonitor, &monitorInfo);
1070 DEVMODE devMode = {};
1071 devMode.dmSize =
sizeof(DEVMODE);
1072 devMode.dmDriverExtra = 0;
1073 EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
1075 if (devMode.dmDisplayFrequency != 0) list.push_back(devMode.dmDisplayFrequency);
1081 std::vector<int> rates = {};
1082 EnumDisplayMonitors(
nullptr,
nullptr, MonitorEnumProc,
reinterpret_cast<LPARAM
>(&rates));
1088 return {
static_cast<uint
>(GetSystemMetrics(SM_CXSCREEN)),
static_cast<uint
>(GetSystemMetrics(SM_CYSCREEN)) };
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()
1297#ifndef PFD_SUPPORT_COMPOSITION
1298# define PFD_SUPPORT_COMPOSITION 0x00008000
1301static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB =
nullptr;
1302static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT =
nullptr;
1303static bool _hasWGLARBCreateContextProfile =
false;
1308 OGLProc ret =
reinterpret_cast<OGLProc
>(wglGetProcAddress(proc));
1309 if (ret ==
nullptr) {
1311 ret =
reinterpret_cast<OGLProc
>(GetProcAddress(GetModuleHandle(L
"opengl32"), proc));
1321static std::optional<std::string_view> SelectPixelFormat(HDC dc)
1323 PIXELFORMATDESCRIPTOR pfd = {
1324 sizeof(PIXELFORMATDESCRIPTOR),
1326 PFD_DRAW_TO_WINDOW |
1327 PFD_SUPPORT_OPENGL |
1332 0, 0, 0, 0, 0, 0, 0, 0,
1340 pfd.dwFlags |= PFD_SUPPORT_COMPOSITION;
1343 int format = ChoosePixelFormat(dc, &pfd);
1344 if (format == 0)
return "No suitable pixel format found";
1345 if (!SetPixelFormat(dc, format, &pfd))
return "Can't set pixel format";
1347 return std::nullopt;
1351static void LoadWGLExtensions()
1358 HWND wnd = CreateWindow(L
"STATIC", L
"dummy", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0,
nullptr,
nullptr, GetModuleHandle(
nullptr),
nullptr);
1359 HDC dc = GetDC(wnd);
1362 if (SelectPixelFormat(dc) == std::nullopt) {
1364 HGLRC rc = wglCreateContext(dc);
1365 if (rc !=
nullptr) {
1366 wglMakeCurrent(dc, rc);
1370#pragma GCC diagnostic push
1371#pragma GCC diagnostic ignored "-Wcast-function-type"
1375 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress(
"wglGetExtensionsStringARB");
1376 if (wglGetExtensionsStringARB !=
nullptr) {
1377 std::string_view wgl_exts = wglGetExtensionsStringARB(dc);
1380 _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress(
"wglCreateContextAttribsARB");
1384 _wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress(
"wglSwapIntervalEXT");
1389#pragma GCC diagnostic pop
1391 wglMakeCurrent(
nullptr,
nullptr);
1392 wglDeleteContext(rc);
1400static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
1402std::optional<std::string_view> VideoDriver_Win32OpenGL::Start(
const StringList ¶m)
1408 LoadWGLExtensions();
1411 this->MakeWindow(_fullscreen);
1414 auto err = this->AllocateContext();
1421 this->driver_info = GetName();
1422 this->driver_info +=
" (";
1424 this->driver_info +=
")";
1426 this->ClientSizeChanged(this->width, this->height,
true);
1428 if (_screen.dst_ptr ==
nullptr) {
1431 return "Can't get pointer to screen buffer";
1434 this->ReleaseVideoPointer();
1440 return std::nullopt;
1443void VideoDriver_Win32OpenGL::Stop()
1445 this->DestroyContext();
1449void VideoDriver_Win32OpenGL::DestroyContext()
1453 wglMakeCurrent(
nullptr,
nullptr);
1454 if (this->gl_rc !=
nullptr) {
1455 wglDeleteContext(this->gl_rc);
1456 this->gl_rc =
nullptr;
1458 if (this->dc !=
nullptr) {
1459 ReleaseDC(this->main_wnd, this->dc);
1464void VideoDriver_Win32OpenGL::ToggleVsync(
bool vsync)
1466 if (_wglSwapIntervalEXT !=
nullptr) {
1467 _wglSwapIntervalEXT(vsync);
1469 Debug(driver, 0,
"OpenGL: Vsync requested, but not supported by driver");
1473std::optional<std::string_view> VideoDriver_Win32OpenGL::AllocateContext()
1475 this->dc = GetDC(this->main_wnd);
1477 auto err = SelectPixelFormat(this->dc);
1478 if (err)
return err;
1483 if (_wglCreateContextAttribsARB !=
nullptr) {
1486 WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
1487 WGL_CONTEXT_MINOR_VERSION_ARB, 5,
1488 WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
1489 _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1492 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1494 if (rc ==
nullptr) {
1498 rc = _wglCreateContextAttribsARB(this->dc,
nullptr, attribs);
1502 if (rc ==
nullptr) {
1504 rc = wglCreateContext(this->dc);
1505 if (rc ==
nullptr)
return "Can't create OpenGL context";
1507 if (!wglMakeCurrent(this->dc, rc))
return "Can't activate GL context";
1515bool VideoDriver_Win32OpenGL::ToggleFullscreen(
bool full_screen)
1517 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1518 this->DestroyContext();
1520 res &= this->AllocateContext() == std::nullopt;
1521 this->ClientSizeChanged(this->width, this->height,
true);
1525bool VideoDriver_Win32OpenGL::AfterBlitterChange()
1528 this->ClientSizeChanged(this->width, this->height,
true);
1532void VideoDriver_Win32OpenGL::PopulateSystemSprites()
1537void VideoDriver_Win32OpenGL::ClearSystemSprites()
1542bool VideoDriver_Win32OpenGL::AllocateBackingStore(
int w,
int h,
bool force)
1544 if (!force && w == _screen.width && h == _screen.height)
return false;
1546 this->width = w = std::max(w, 64);
1547 this->height = h = std::max(h, 64);
1549 if (this->gl_rc ==
nullptr)
return false;
1551 if (_screen.dst_ptr !=
nullptr) this->ReleaseVideoPointer();
1553 this->dirty_rect = {};
1555 SwapBuffers(this->dc);
1556 _screen.dst_ptr = this->GetVideoPointer();
1561void *VideoDriver_Win32OpenGL::GetVideoPointer()
1569void VideoDriver_Win32OpenGL::ReleaseVideoPointer()
1573 this->dirty_rect = {};
1574 _screen.dst_ptr =
nullptr;
1575 this->anim_buffer =
nullptr;
1578void VideoDriver_Win32OpenGL::Paint()
1597 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()
Colour depth to use for fullscreen display modes.
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: