12 #include "../stdafx.h"
13 #include "../openttd.h"
14 #include "../gfx_func.h"
15 #include "../os/windows/win32.h"
17 #include "../blitter/factory.hpp"
18 #include "../network/network.h"
19 #include "../core/math_func.hpp"
20 #include "../core/random_func.hpp"
21 #include "../texteff.hpp"
22 #include "../thread/thread.h"
23 #include "../progress.h"
24 #include "../window_gui.h"
25 #include "../window_func.h"
30 #include "../safeguards.h"
33 #ifndef MAPVK_VK_TO_CHAR
34 #define MAPVK_VK_TO_CHAR (2)
52 bool _force_full_redraw;
53 bool _window_maximize;
56 #if !defined(WINCE) || _WIN32_WCE >= 0x400
73 static void MakePalette()
75 LOGPALETTE *pal = (LOGPALETTE*)alloca(
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY));
77 pal->palVersion = 0x300;
78 pal->palNumEntries = 256;
80 for (uint i = 0; i != 256; i++) {
84 pal->palPalEntry[i].peFlags = 0;
87 _wnd.gdi_palette = CreatePalette(pal);
88 if (_wnd.gdi_palette == NULL)
usererror(
"CreatePalette failed!\n");
95 static void UpdatePalette(HDC dc, uint start, uint count)
100 for (i = 0; i != count; i++) {
101 rgb[i].rgbRed = _local_palette.
palette[start + i].r;
102 rgb[i].rgbGreen = _local_palette.
palette[start + i].g;
103 rgb[i].rgbBlue = _local_palette.
palette[start + i].b;
104 rgb[i].rgbReserved = 0;
107 SetDIBColorTable(dc, start, count, rgb);
110 bool VideoDriver_Win32::ClaimMousePointer()
112 MyShowCursor(
false,
true);
122 #define AS(x, z) {x, 0, z}
123 #define AM(x, y, z, w) {x, y - x, z}
127 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
129 AM(
'A',
'Z',
'A',
'Z'),
130 AM(
'0',
'9',
'0',
'9'),
132 AS(VK_ESCAPE, WKC_ESC),
133 AS(VK_PAUSE, WKC_PAUSE),
134 AS(VK_BACK, WKC_BACKSPACE),
135 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
137 AS(VK_SPACE, WKC_SPACE),
138 AS(VK_RETURN, WKC_RETURN),
142 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
145 AM(VK_NUMPAD0, VK_NUMPAD9,
'0',
'9'),
146 AS(VK_DIVIDE, WKC_NUM_DIV),
147 AS(VK_MULTIPLY, WKC_NUM_MUL),
148 AS(VK_SUBTRACT, WKC_NUM_MINUS),
149 AS(VK_ADD, WKC_NUM_PLUS),
150 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
166 static uint MapWindowsKey(uint sym)
171 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
172 if ((uint)(sym - map->vk_from) <= map->vk_count) {
173 key = sym - map->vk_from + map->map_to;
178 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
179 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
180 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
184 static bool AllocateDibSection(
int w,
int h,
bool force =
false);
186 static void ClientSizeChanged(
int w,
int h)
189 if (AllocateDibSection(w, h)) {
204 int RedrawScreenDebug()
209 HPALETTE old_palette;
213 dc = GetDC(_wnd.main_wnd);
214 dc2 = CreateCompatibleDC(dc);
216 old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
217 old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
218 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
219 SelectPalette(dc, old_palette, TRUE);
220 SelectObject(dc2, old_bmp);
222 ReleaseDC(_wnd.main_wnd, dc);
229 #if !defined(WM_MOUSELEAVE)
230 #define WM_MOUSELEAVE 0x02A3
232 #define TID_POLLMOUSE 1
233 #define MOUSE_POLL_DELAY 75
235 static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time)
243 GetClientRect(hwnd, &rc);
244 MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2);
247 if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) {
248 KillTimer(hwnd, event);
249 PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L);
260 _fullscreen = full_screen;
263 if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) {
264 DestroyWindow(_wnd.main_wnd);
274 memset(&settings, 0,
sizeof(settings));
275 settings.dmSize =
sizeof(settings);
280 (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0);
282 settings.dmPelsWidth = _wnd.width_org;
283 settings.dmPelsHeight = _wnd.height_org;
284 settings.dmDisplayFrequency = _display_hz;
287 if (settings.dmBitsPerPel == 8 &&
288 (_support8bpp !=
S8BPP_HARDWARE || ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL)) {
289 settings.dmBitsPerPel = 32;
293 if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
295 GetWindowRect(GetDesktopWindow(), &r);
298 if ((
int)settings.dmPelsWidth != r.right - r.left || (
int)settings.dmPelsHeight != r.bottom - r.top) {
303 if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
307 }
else if (_wnd.fullscreen) {
309 ChangeDisplaySettings(NULL, 0);
311 _wnd.width = _bck_resolution.width;
312 _wnd.height = _bck_resolution.height;
318 DWORD style, showstyle;
321 showstyle = SW_SHOWNORMAL;
322 _wnd.fullscreen = full_screen;
323 if (_wnd.fullscreen) {
325 SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org);
327 style = WS_OVERLAPPEDWINDOW;
329 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
330 SetRect(&r, 0, 0, _wnd.width, _wnd.height);
334 AdjustWindowRect(&r, style, FALSE);
336 w = r.right - r.left;
337 h = r.bottom - r.top;
339 if (_wnd.main_wnd != NULL) {
340 if (!_window_maximize) SetWindowPos(_wnd.main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
342 TCHAR Windowtitle[50];
343 int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
344 int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
346 _sntprintf(Windowtitle,
lengthof(Windowtitle), _T(
"OpenTTD %s"), MB_TO_WIDE(_openttd_revision));
348 _wnd.main_wnd = CreateWindow(_T(
"OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0);
349 if (_wnd.main_wnd == NULL)
usererror(
"CreateWindow failed");
350 ShowWindow(_wnd.main_wnd, showstyle);
363 HDC dc2 = CreateCompatibleDC(dc);
364 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
365 HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
388 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
389 SelectPalette(dc, old_palette, TRUE);
390 SelectObject(dc2, old_bmp);
394 static void PaintWindowThread(
void *)
406 ClientToScreen(_wnd.main_wnd, &pt);
407 OffsetRect(&_wnd.update_rect, pt.x, pt.y);
411 HRGN rgn = CreateRectRgnIndirect(&_wnd.update_rect);
412 HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
417 SetRectEmpty(&_wnd.update_rect);
418 ReleaseDC(_wnd.main_wnd, dc);
427 _draw_thread->
Exit();
433 #if !defined(UNICODE)
434 static char prev_char = 0;
436 char input[2] = {(char)charcode, 0};
439 if (prev_char != 0) {
441 input[0] = prev_char;
442 input[1] = (char)charcode;
444 }
else if (IsDBCSLeadByte(charcode)) {
446 prev_char = charcode;
452 int len = MultiByteToWideChar(CP_ACP, 0, input, input_len, w, 2);
463 DEBUG(driver, 1,
"Invalid DBCS character sequence encountered, dropping input");
468 static WChar prev_char = 0;
472 if (prev_char != 0)
DEBUG(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
473 prev_char = charcode;
478 if (prev_char != 0) {
482 DEBUG(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
493 #if !defined(WINCE) || _WIN32_WCE >= 0x400
497 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
503 HIMC hIMC = ImmGetContext(hwnd);
506 cf.dwStyle = CFS_POINT;
511 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
512 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
514 cf.ptCurrentPos.x = 0;
515 cf.ptCurrentPos.y = 0;
517 ImmSetCompositionWindow(hIMC, &cf);
519 ImmReleaseContext(hwnd, hIMC);
525 HIMC hIMC = ImmGetContext(hwnd);
529 cf.dwStyle = CFS_EXCLUDE;
533 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
534 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
536 cf.rcArea.left = _focused_window->
left;
537 cf.rcArea.top = _focused_window->
top;
538 cf.rcArea.right = _focused_window->
left + _focused_window->
width;
539 cf.rcArea.bottom = _focused_window->
top + _focused_window->
height;
547 cf.ptCurrentPos.x = 0;
548 cf.ptCurrentPos.y = 0;
549 SetRectEmpty(&cf.rcArea);
551 ImmSetCandidateWindow(hIMC, &cf);
553 ImmReleaseContext(hwnd, hIMC);
559 HIMC hIMC = ImmGetContext(hwnd);
562 if (lParam & GCS_RESULTSTR) {
564 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
565 TCHAR *str = (TCHAR *)_alloca(len +
sizeof(TCHAR));
566 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str, len);
567 str[len /
sizeof(TCHAR)] =
'\0';
577 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
582 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0);
583 TCHAR *str = (TCHAR *)_alloca(len +
sizeof(TCHAR));
584 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str, len);
585 str[len /
sizeof(TCHAR)] =
'\0';
588 static char utf8_buf[1024];
592 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0);
593 const char *caret = utf8_buf;
594 for (
const TCHAR *c = str; *c !=
'\0' && *caret !=
'\0' && caret_bytes > 0; c++, caret_bytes--) {
599 if (IsDBCSLeadByte(*c)) {
612 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
615 ImmReleaseContext(hwnd, hIMC);
617 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
623 HIMC hIMC = ImmGetContext(hwnd);
624 if (hIMC != NULL) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
625 ImmReleaseContext(hwnd, hIMC);
639 static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
641 static uint32 keycode = 0;
642 static bool console =
false;
643 static bool in_sizemove =
false;
647 SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
649 #if !defined(WINCE) || _WIN32_WCE >= 0x400
654 case WM_ENTERSIZEMOVE:
658 case WM_EXITSIZEMOVE:
666 GetUpdateRect(hwnd, &r, FALSE);
667 UnionRect(&_wnd.update_rect, &_wnd.update_rect, &r);
670 ValidateRect(hwnd, NULL);
675 BeginPaint(hwnd, &ps);
681 case WM_PALETTECHANGED:
682 if ((HWND)wParam == hwnd)
return 0;
685 case WM_QUERYNEWPALETTE: {
686 HDC hDC = GetWindowDC(hwnd);
687 HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
688 UINT nChanged = RealizePalette(hDC);
690 SelectPalette(hDC, hOldPalette, TRUE);
691 ReleaseDC(hwnd, hDC);
692 if (nChanged != 0) InvalidateRect(hwnd, NULL, FALSE);
697 HandleExitGameRequest();
738 int x = (int16)LOWORD(lParam);
739 int y = (int16)HIWORD(lParam);
747 SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
751 int dx = x - _cursor.pos.x;
752 int dy = y - _cursor.pos.y;
753 if (dx != 0 || dy != 0) {
754 _cursor.
delta.x = dx;
755 _cursor.
delta.y = dy;
757 pt.x = _cursor.pos.x;
758 pt.y = _cursor.pos.y;
760 ClientToScreen(hwnd, &pt);
761 SetCursorPos(pt.x, pt.y);
764 _cursor.
delta.x = x - _cursor.pos.x;
765 _cursor.
delta.y = y - _cursor.pos.y;
768 _cursor.
dirty =
true;
775 #if !defined(WINCE) || _WIN32_WCE >= 0x400
776 case WM_INPUTLANGCHANGE:
780 case WM_IME_SETCONTEXT:
785 case WM_IME_STARTCOMPOSITION:
790 case WM_IME_COMPOSITION:
793 case WM_IME_ENDCOMPOSITION:
803 #if !defined(UNICODE)
805 if (
GB(wParam, 8, 8) != 0) {
815 console =
GB(lParam, 16, 8) == 41;
819 uint scancode =
GB(lParam, 16, 8);
820 uint charcode = wParam;
824 if (console && scancode == 41) {
831 uint cur_keycode = keycode;
839 uint scancode =
GB(lParam, 16, 8);
840 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
844 if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
845 if ((msg.message == WM_CHAR || msg.message == WM_DEADCHAR) &&
GB(lParam, 16, 8) ==
GB(msg.lParam, 16, 8)) {
850 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
859 if (
HasBit(charcode, 31) && !console) {
860 if (scancode == 41) {
869 uint cur_keycode = keycode;
879 ToggleFullScreen(!_wnd.fullscreen);
896 if (wParam != SIZE_MINIMIZED) {
899 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
900 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
901 ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
907 RECT *r = (RECT*)lParam;
911 SetRect(&r2, 0, 0, 0, 0);
912 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
914 w = r->right - r->left - (r2.right - r2.left);
915 h = r->bottom - r->top - (r2.bottom - r2.top);
918 SetRect(&r2, 0, 0, w, h);
920 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
921 w = r2.right - r2.left;
922 h = r2.bottom - r2.top;
926 r->bottom = r->top + h;
929 case WMSZ_BOTTOMLEFT:
930 r->bottom = r->top + h;
931 r->left = r->right - w;
934 case WMSZ_BOTTOMRIGHT:
935 r->bottom = r->top + h;
936 r->right = r->left + w;
940 r->left = r->right - w;
944 r->right = r->left + w;
948 r->top = r->bottom - h;
952 r->top = r->bottom - h;
953 r->left = r->right - w;
957 r->top = r->bottom - h;
958 r->right = r->left + w;
966 #if !defined(WM_MOUSEWHEEL)
967 # define WM_MOUSEWHEEL 0x020A
969 #if !defined(GET_WHEEL_DELTA_WPARAM)
970 # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
973 case WM_MOUSEWHEEL: {
974 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
978 }
else if (delta > 0) {
986 _wnd.has_focus =
true;
991 _wnd.has_focus =
false;
997 if (_exit_game)
break;
999 bool active = (LOWORD(wParam) != WA_INACTIVE);
1000 bool minimized = (HIWORD(wParam) != 0);
1001 if (_wnd.fullscreen) {
1002 if (active && minimized) {
1004 ShowWindow(hwnd, SW_RESTORE);
1006 }
else if (!active && !minimized) {
1008 ShowWindow(hwnd, SW_MINIMIZE);
1009 ChangeDisplaySettings(NULL, 0);
1017 return DefWindowProc(hwnd, msg, wParam, lParam);
1020 static void RegisterWndClass()
1022 static bool registered =
false;
1025 HINSTANCE hinst = GetModuleHandle(NULL);
1032 LoadIcon(hinst, MAKEINTRESOURCE(100)),
1033 LoadCursor(NULL, IDC_ARROW),
1040 if (!RegisterClass(&wnd))
usererror(
"RegisterClass failed");
1044 static bool AllocateDibSection(
int w,
int h,
bool force)
1053 if (bpp == 0)
usererror(
"Can't use a blitter that blits 0 bpp for normal visuals");
1055 if (!force && w == _screen.width && h == _screen.height)
return false;
1057 bi = (BITMAPINFO*)alloca(
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256);
1058 memset(bi, 0,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256);
1059 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1061 bi->bmiHeader.biWidth = _wnd.width = w;
1062 bi->bmiHeader.biHeight = -(_wnd.height = h);
1064 bi->bmiHeader.biPlanes = 1;
1066 bi->bmiHeader.biCompression = BI_RGB;
1068 if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
1071 _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.buffer_bits, NULL, 0);
1072 if (_wnd.dib_sect == NULL)
usererror(
"CreateDIBSection failed");
1076 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1078 _screen.dst_ptr = _wnd.buffer_bits;
1083 static const Dimension default_resolutions[] = {
1097 static void FindResolutions()
1113 for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) {
1114 if (dm.dmBitsPerPel == bpp &&
1115 dm.dmPelsWidth >= 640 && dm.dmPelsHeight >= 480) {
1118 for (j = 0; j < n; j++) {
1137 memcpy(
_resolutions, default_resolutions,
sizeof(default_resolutions));
1149 memset(&_wnd, 0,
sizeof(_wnd));
1175 DeleteObject(_wnd.gdi_palette);
1176 DeleteObject(_wnd.dib_sect);
1177 DestroyWindow(_wnd.main_wnd);
1180 if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0);
1187 RECT r = { left, top, left + width, top + height };
1189 InvalidateRect(_wnd.main_wnd, &r, FALSE);
1192 static void CheckPaletteAnim()
1197 InvalidateRect(_wnd.main_wnd, NULL, FALSE);
1203 uint32 cur_ticks = GetTickCount();
1204 uint32 last_cur_ticks = cur_ticks;
1225 DEBUG(driver, 1,
"Threaded drawing enabled");
1233 _wnd.running =
true;
1237 uint32 prev_cur_ticks = cur_ticks;
1239 while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
1240 InteractiveRandom();
1243 DispatchMessage(&mesg);
1245 if (_exit_game)
return;
1248 if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 &&
1251 if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 &&
1255 }
else if (_fast_forward & 2) {
1259 cur_ticks = GetTickCount();
1260 if (cur_ticks >= next_tick || (_fast_forward && !
_pause_mode) || cur_ticks < prev_cur_ticks) {
1262 last_cur_ticks = cur_ticks;
1267 _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0;
1271 if (_wnd.has_focus) {
1273 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
1274 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
1275 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
1276 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
1320 _draw_thread->
Join();
1331 if (_window_maximize) ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL);
1333 _wnd.width = _wnd.width_org = w;
1334 _wnd.height = _wnd.height_org = h;
1337 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
1345 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
1352 bool ret = AllocateDibSection(_screen.width, _screen.height,
true) && this->
MakeWindow(_fullscreen);
1353 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
1363 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);