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)
38 #define PM_QS_INPUT 0x20000
56 bool _force_full_redraw;
57 bool _window_maximize;
60 #if !defined(WINCE) || _WIN32_WCE >= 0x400
77 static void MakePalette()
79 LOGPALETTE *pal = (LOGPALETTE*)alloca(
sizeof(LOGPALETTE) + (256 - 1) *
sizeof(PALETTEENTRY));
81 pal->palVersion = 0x300;
82 pal->palNumEntries = 256;
84 for (uint i = 0; i != 256; i++) {
88 pal->palPalEntry[i].peFlags = 0;
91 _wnd.gdi_palette = CreatePalette(pal);
92 if (_wnd.gdi_palette == NULL)
usererror(
"CreatePalette failed!\n");
99 static void UpdatePalette(HDC dc, uint start, uint count)
104 for (i = 0; i != count; i++) {
105 rgb[i].rgbRed = _local_palette.
palette[start + i].r;
106 rgb[i].rgbGreen = _local_palette.
palette[start + i].g;
107 rgb[i].rgbBlue = _local_palette.
palette[start + i].b;
108 rgb[i].rgbReserved = 0;
111 SetDIBColorTable(dc, start, count, rgb);
114 bool VideoDriver_Win32::ClaimMousePointer()
116 MyShowCursor(
false,
true);
126 #define AS(x, z) {x, 0, z}
127 #define AM(x, y, z, w) {x, y - x, z}
131 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
133 AM(
'A',
'Z',
'A',
'Z'),
134 AM(
'0',
'9',
'0',
'9'),
136 AS(VK_ESCAPE, WKC_ESC),
137 AS(VK_PAUSE, WKC_PAUSE),
138 AS(VK_BACK, WKC_BACKSPACE),
139 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
141 AS(VK_SPACE, WKC_SPACE),
142 AS(VK_RETURN, WKC_RETURN),
146 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
149 AM(VK_NUMPAD0, VK_NUMPAD9,
'0',
'9'),
150 AS(VK_DIVIDE, WKC_NUM_DIV),
151 AS(VK_MULTIPLY, WKC_NUM_MUL),
152 AS(VK_SUBTRACT, WKC_NUM_MINUS),
153 AS(VK_ADD, WKC_NUM_PLUS),
154 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
170 static uint MapWindowsKey(uint sym)
175 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
176 if ((uint)(sym - map->vk_from) <= map->vk_count) {
177 key = sym - map->vk_from + map->map_to;
182 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
183 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
184 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
188 static bool AllocateDibSection(
int w,
int h,
bool force =
false);
190 static void ClientSizeChanged(
int w,
int h)
193 if (AllocateDibSection(w, h)) {
208 int RedrawScreenDebug()
213 HPALETTE old_palette;
217 dc = GetDC(_wnd.main_wnd);
218 dc2 = CreateCompatibleDC(dc);
220 old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
221 old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
222 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
223 SelectPalette(dc, old_palette, TRUE);
224 SelectObject(dc2, old_bmp);
226 ReleaseDC(_wnd.main_wnd, dc);
233 #if !defined(WM_MOUSELEAVE)
234 #define WM_MOUSELEAVE 0x02A3
236 #define TID_POLLMOUSE 1
237 #define MOUSE_POLL_DELAY 75
239 static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time)
247 GetClientRect(hwnd, &rc);
248 MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2);
251 if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) {
252 KillTimer(hwnd, event);
253 PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L);
264 _fullscreen = full_screen;
267 if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) {
268 DestroyWindow(_wnd.main_wnd);
278 memset(&settings, 0,
sizeof(settings));
279 settings.dmSize =
sizeof(settings);
284 (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0);
286 settings.dmPelsWidth = _wnd.width_org;
287 settings.dmPelsHeight = _wnd.height_org;
288 settings.dmDisplayFrequency = _display_hz;
291 if (settings.dmBitsPerPel == 8 &&
292 (_support8bpp !=
S8BPP_HARDWARE || ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL)) {
293 settings.dmBitsPerPel = 32;
297 if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
299 GetWindowRect(GetDesktopWindow(), &r);
302 if ((
int)settings.dmPelsWidth != r.right - r.left || (
int)settings.dmPelsHeight != r.bottom - r.top) {
307 if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
311 }
else if (_wnd.fullscreen) {
313 ChangeDisplaySettings(NULL, 0);
315 _wnd.width = _bck_resolution.width;
316 _wnd.height = _bck_resolution.height;
322 DWORD style, showstyle;
325 showstyle = SW_SHOWNORMAL;
326 _wnd.fullscreen = full_screen;
327 if (_wnd.fullscreen) {
329 SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org);
331 style = WS_OVERLAPPEDWINDOW;
333 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
334 SetRect(&r, 0, 0, _wnd.width, _wnd.height);
338 AdjustWindowRect(&r, style, FALSE);
340 w = r.right - r.left;
341 h = r.bottom - r.top;
343 if (_wnd.main_wnd != NULL) {
344 if (!_window_maximize) SetWindowPos(_wnd.main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
346 TCHAR Windowtitle[50];
347 int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
348 int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
350 _sntprintf(Windowtitle,
lengthof(Windowtitle), _T(
"OpenTTD %s"), MB_TO_WIDE(_openttd_revision));
352 _wnd.main_wnd = CreateWindow(_T(
"OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0);
353 if (_wnd.main_wnd == NULL)
usererror(
"CreateWindow failed");
354 ShowWindow(_wnd.main_wnd, showstyle);
367 HDC dc2 = CreateCompatibleDC(dc);
368 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
369 HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
392 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
393 SelectPalette(dc, old_palette, TRUE);
394 SelectObject(dc2, old_bmp);
398 static void PaintWindowThread(
void *)
410 ClientToScreen(_wnd.main_wnd, &pt);
411 OffsetRect(&_wnd.update_rect, pt.x, pt.y);
415 HRGN rgn = CreateRectRgnIndirect(&_wnd.update_rect);
416 HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
421 SetRectEmpty(&_wnd.update_rect);
422 ReleaseDC(_wnd.main_wnd, dc);
431 _draw_thread->
Exit();
437 #if !defined(UNICODE)
438 static char prev_char = 0;
440 char input[2] = {(char)charcode, 0};
443 if (prev_char != 0) {
445 input[0] = prev_char;
446 input[1] = (char)charcode;
448 }
else if (IsDBCSLeadByte(charcode)) {
450 prev_char = charcode;
456 int len = MultiByteToWideChar(CP_ACP, 0, input, input_len, w, 2);
467 DEBUG(driver, 1,
"Invalid DBCS character sequence encountered, dropping input");
472 static WChar prev_char = 0;
476 if (prev_char != 0)
DEBUG(driver, 1,
"Got two UTF-16 lead surrogates, dropping the first one");
477 prev_char = charcode;
482 if (prev_char != 0) {
486 DEBUG(driver, 1,
"Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
497 #if !defined(WINCE) || _WIN32_WCE >= 0x400
501 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
507 HIMC hIMC = ImmGetContext(hwnd);
510 cf.dwStyle = CFS_POINT;
515 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
516 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
518 cf.ptCurrentPos.x = 0;
519 cf.ptCurrentPos.y = 0;
521 ImmSetCompositionWindow(hIMC, &cf);
523 ImmReleaseContext(hwnd, hIMC);
529 HIMC hIMC = ImmGetContext(hwnd);
533 cf.dwStyle = CFS_EXCLUDE;
537 cf.ptCurrentPos.x = _focused_window->
left + pt.x;
538 cf.ptCurrentPos.y = _focused_window->
top + pt.y;
540 cf.rcArea.left = _focused_window->
left;
541 cf.rcArea.top = _focused_window->
top;
542 cf.rcArea.right = _focused_window->
left + _focused_window->
width;
543 cf.rcArea.bottom = _focused_window->
top + _focused_window->
height;
551 cf.ptCurrentPos.x = 0;
552 cf.ptCurrentPos.y = 0;
553 SetRectEmpty(&cf.rcArea);
555 ImmSetCandidateWindow(hIMC, &cf);
557 ImmReleaseContext(hwnd, hIMC);
563 HIMC hIMC = ImmGetContext(hwnd);
566 if (lParam & GCS_RESULTSTR) {
568 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
569 TCHAR *str = (TCHAR *)_alloca(len +
sizeof(TCHAR));
570 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str, len);
571 str[len /
sizeof(TCHAR)] =
'\0';
581 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
586 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0);
587 TCHAR *str = (TCHAR *)_alloca(len +
sizeof(TCHAR));
588 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str, len);
589 str[len /
sizeof(TCHAR)] =
'\0';
592 static char utf8_buf[1024];
596 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0);
597 const char *caret = utf8_buf;
598 for (
const TCHAR *c = str; *c !=
'\0' && *caret !=
'\0' && caret_bytes > 0; c++, caret_bytes--) {
603 if (IsDBCSLeadByte(*c)) {
616 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
619 ImmReleaseContext(hwnd, hIMC);
621 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
627 HIMC hIMC = ImmGetContext(hwnd);
628 if (hIMC != NULL) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
629 ImmReleaseContext(hwnd, hIMC);
643 static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
645 static uint32 keycode = 0;
646 static bool console =
false;
647 static bool in_sizemove =
false;
651 SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
653 #if !defined(WINCE) || _WIN32_WCE >= 0x400
658 case WM_ENTERSIZEMOVE:
662 case WM_EXITSIZEMOVE:
670 GetUpdateRect(hwnd, &r, FALSE);
671 UnionRect(&_wnd.update_rect, &_wnd.update_rect, &r);
674 ValidateRect(hwnd, NULL);
679 BeginPaint(hwnd, &ps);
685 case WM_PALETTECHANGED:
686 if ((HWND)wParam == hwnd)
return 0;
689 case WM_QUERYNEWPALETTE: {
690 HDC hDC = GetWindowDC(hwnd);
691 HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
692 UINT nChanged = RealizePalette(hDC);
694 SelectPalette(hDC, hOldPalette, TRUE);
695 ReleaseDC(hwnd, hDC);
696 if (nChanged != 0) InvalidateRect(hwnd, NULL, FALSE);
701 HandleExitGameRequest();
742 int x = (int16)LOWORD(lParam);
743 int y = (int16)HIWORD(lParam);
750 SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
757 while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
758 x = (int16)LOWORD(m.lParam);
759 y = (int16)HIWORD(m.lParam);
765 pt.x = _cursor.
pos.x;
766 pt.y = _cursor.
pos.y;
767 ClientToScreen(hwnd, &pt);
768 SetCursorPos(pt.x, pt.y);
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);
1351 return AllocateDibSection(_screen.width, _screen.height,
true) && this->
MakeWindow(_fullscreen);
1361 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
1370 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);