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 pt.x = _cursor.
pos.x;
752 pt.y = _cursor.
pos.y;
753 ClientToScreen(hwnd, &pt);
754 SetCursorPos(pt.x, pt.y);
761 #if !defined(WINCE) || _WIN32_WCE >= 0x400
762 case WM_INPUTLANGCHANGE:
766 case WM_IME_SETCONTEXT:
771 case WM_IME_STARTCOMPOSITION:
776 case WM_IME_COMPOSITION:
779 case WM_IME_ENDCOMPOSITION:
789 #if !defined(UNICODE)
791 if (
GB(wParam, 8, 8) != 0) {
801 console =
GB(lParam, 16, 8) == 41;
805 uint scancode =
GB(lParam, 16, 8);
806 uint charcode = wParam;
810 if (console && scancode == 41) {
817 uint cur_keycode = keycode;
825 uint scancode =
GB(lParam, 16, 8);
826 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
830 if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
831 if ((msg.message == WM_CHAR || msg.message == WM_DEADCHAR) &&
GB(lParam, 16, 8) ==
GB(msg.lParam, 16, 8)) {
836 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
845 if (
HasBit(charcode, 31) && !console) {
846 if (scancode == 41) {
855 uint cur_keycode = keycode;
865 ToggleFullScreen(!_wnd.fullscreen);
882 if (wParam != SIZE_MINIMIZED) {
885 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
886 if (_window_maximize || _fullscreen) _bck_resolution =
_cur_resolution;
887 ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
893 RECT *r = (RECT*)lParam;
897 SetRect(&r2, 0, 0, 0, 0);
898 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
900 w = r->right - r->left - (r2.right - r2.left);
901 h = r->bottom - r->top - (r2.bottom - r2.top);
904 SetRect(&r2, 0, 0, w, h);
906 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
907 w = r2.right - r2.left;
908 h = r2.bottom - r2.top;
912 r->bottom = r->top + h;
915 case WMSZ_BOTTOMLEFT:
916 r->bottom = r->top + h;
917 r->left = r->right - w;
920 case WMSZ_BOTTOMRIGHT:
921 r->bottom = r->top + h;
922 r->right = r->left + w;
926 r->left = r->right - w;
930 r->right = r->left + w;
934 r->top = r->bottom - h;
938 r->top = r->bottom - h;
939 r->left = r->right - w;
943 r->top = r->bottom - h;
944 r->right = r->left + w;
952 #if !defined(WM_MOUSEWHEEL)
953 # define WM_MOUSEWHEEL 0x020A
955 #if !defined(GET_WHEEL_DELTA_WPARAM)
956 # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
959 case WM_MOUSEWHEEL: {
960 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
964 }
else if (delta > 0) {
972 _wnd.has_focus =
true;
977 _wnd.has_focus =
false;
983 if (_exit_game)
break;
985 bool active = (LOWORD(wParam) != WA_INACTIVE);
986 bool minimized = (HIWORD(wParam) != 0);
987 if (_wnd.fullscreen) {
988 if (active && minimized) {
990 ShowWindow(hwnd, SW_RESTORE);
992 }
else if (!active && !minimized) {
994 ShowWindow(hwnd, SW_MINIMIZE);
995 ChangeDisplaySettings(NULL, 0);
1003 return DefWindowProc(hwnd, msg, wParam, lParam);
1006 static void RegisterWndClass()
1008 static bool registered =
false;
1011 HINSTANCE hinst = GetModuleHandle(NULL);
1018 LoadIcon(hinst, MAKEINTRESOURCE(100)),
1019 LoadCursor(NULL, IDC_ARROW),
1026 if (!RegisterClass(&wnd))
usererror(
"RegisterClass failed");
1030 static bool AllocateDibSection(
int w,
int h,
bool force)
1039 if (bpp == 0)
usererror(
"Can't use a blitter that blits 0 bpp for normal visuals");
1041 if (!force && w == _screen.width && h == _screen.height)
return false;
1043 bi = (BITMAPINFO*)alloca(
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256);
1044 memset(bi, 0,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * 256);
1045 bi->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
1047 bi->bmiHeader.biWidth = _wnd.width = w;
1048 bi->bmiHeader.biHeight = -(_wnd.height = h);
1050 bi->bmiHeader.biPlanes = 1;
1052 bi->bmiHeader.biCompression = BI_RGB;
1054 if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
1057 _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.buffer_bits, NULL, 0);
1058 if (_wnd.dib_sect == NULL)
usererror(
"CreateDIBSection failed");
1062 _screen.pitch = (bpp == 8) ?
Align(w, 4) : w;
1064 _screen.dst_ptr = _wnd.buffer_bits;
1069 static const Dimension default_resolutions[] = {
1083 static void FindResolutions()
1099 for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) {
1100 if (dm.dmBitsPerPel == bpp &&
1101 dm.dmPelsWidth >= 640 && dm.dmPelsHeight >= 480) {
1104 for (j = 0; j < n; j++) {
1123 memcpy(
_resolutions, default_resolutions,
sizeof(default_resolutions));
1135 memset(&_wnd, 0,
sizeof(_wnd));
1161 DeleteObject(_wnd.gdi_palette);
1162 DeleteObject(_wnd.dib_sect);
1163 DestroyWindow(_wnd.main_wnd);
1166 if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0);
1173 RECT r = { left, top, left + width, top + height };
1175 InvalidateRect(_wnd.main_wnd, &r, FALSE);
1178 static void CheckPaletteAnim()
1183 InvalidateRect(_wnd.main_wnd, NULL, FALSE);
1189 uint32 cur_ticks = GetTickCount();
1190 uint32 last_cur_ticks = cur_ticks;
1211 DEBUG(driver, 1,
"Threaded drawing enabled");
1219 _wnd.running =
true;
1223 uint32 prev_cur_ticks = cur_ticks;
1225 while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
1226 InteractiveRandom();
1229 DispatchMessage(&mesg);
1231 if (_exit_game)
return;
1234 if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 &&
1237 if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 &&
1241 }
else if (_fast_forward & 2) {
1245 cur_ticks = GetTickCount();
1246 if (cur_ticks >= next_tick || (_fast_forward && !
_pause_mode) || cur_ticks < prev_cur_ticks) {
1248 last_cur_ticks = cur_ticks;
1253 _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0;
1257 if (_wnd.has_focus) {
1259 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
1260 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
1261 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
1262 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
1306 _draw_thread->
Join();
1317 if (_window_maximize) ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL);
1319 _wnd.width = _wnd.width_org = w;
1320 _wnd.height = _wnd.height_org = h;
1323 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
1331 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
1338 bool ret = AllocateDibSection(_screen.width, _screen.height,
true) && this->
MakeWindow(_fullscreen);
1339 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
1349 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);