00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../openttd.h"
00014 #include "../gfx_func.h"
00015 #include "../os/windows/win32.h"
00016 #include "../rev.h"
00017 #include "../blitter/factory.hpp"
00018 #include "../network/network.h"
00019 #include "../core/math_func.hpp"
00020 #include "../core/random_func.hpp"
00021 #include "../texteff.hpp"
00022 #include "../thread/thread.h"
00023 #include "../progress.h"
00024 #include "../window_gui.h"
00025 #include "../window_func.h"
00026 #include "win32_v.h"
00027 #include <windows.h>
00028 #include <imm.h>
00029
00030
00031 #ifndef MAPVK_VK_TO_CHAR
00032 #define MAPVK_VK_TO_CHAR (2)
00033 #endif
00034
00035 static struct {
00036 HWND main_wnd;
00037 HBITMAP dib_sect;
00038 void *buffer_bits;
00039 HPALETTE gdi_palette;
00040 RECT update_rect;
00041 int width;
00042 int height;
00043 int width_org;
00044 int height_org;
00045 bool fullscreen;
00046 bool has_focus;
00047 bool running;
00048 } _wnd;
00049
00050 bool _force_full_redraw;
00051 bool _window_maximize;
00052 uint _display_hz;
00053 uint _fullscreen_bpp;
00054 static Dimension _bck_resolution;
00055 #if !defined(WINCE) || _WIN32_WCE >= 0x400
00056 DWORD _imm_props;
00057 #endif
00058
00060 static bool _draw_threaded;
00062 static ThreadObject *_draw_thread = NULL;
00064 static ThreadMutex *_draw_mutex = NULL;
00066 static HANDLE _draw_thread_initialized = NULL;
00068 static volatile bool _draw_continue;
00070 static Palette _local_palette;
00071
00072 static void MakePalette()
00073 {
00074 LOGPALETTE *pal = (LOGPALETTE*)alloca(sizeof(LOGPALETTE) + (256 - 1) * sizeof(PALETTEENTRY));
00075
00076 pal->palVersion = 0x300;
00077 pal->palNumEntries = 256;
00078
00079 for (uint i = 0; i != 256; i++) {
00080 pal->palPalEntry[i].peRed = _cur_palette.palette[i].r;
00081 pal->palPalEntry[i].peGreen = _cur_palette.palette[i].g;
00082 pal->palPalEntry[i].peBlue = _cur_palette.palette[i].b;
00083 pal->palPalEntry[i].peFlags = 0;
00084
00085 }
00086 _wnd.gdi_palette = CreatePalette(pal);
00087 if (_wnd.gdi_palette == NULL) usererror("CreatePalette failed!\n");
00088
00089 _cur_palette.first_dirty = 0;
00090 _cur_palette.count_dirty = 256;
00091 _local_palette = _cur_palette;
00092 }
00093
00094 static void UpdatePalette(HDC dc, uint start, uint count)
00095 {
00096 RGBQUAD rgb[256];
00097 uint i;
00098
00099 for (i = 0; i != count; i++) {
00100 rgb[i].rgbRed = _local_palette.palette[start + i].r;
00101 rgb[i].rgbGreen = _local_palette.palette[start + i].g;
00102 rgb[i].rgbBlue = _local_palette.palette[start + i].b;
00103 rgb[i].rgbReserved = 0;
00104 }
00105
00106 SetDIBColorTable(dc, start, count, rgb);
00107 }
00108
00109 bool VideoDriver_Win32::ClaimMousePointer()
00110 {
00111 MyShowCursor(false, true);
00112 return true;
00113 }
00114
00115 struct VkMapping {
00116 byte vk_from;
00117 byte vk_count;
00118 byte map_to;
00119 };
00120
00121 #define AS(x, z) {x, 0, z}
00122 #define AM(x, y, z, w) {x, y - x, z}
00123
00124 static const VkMapping _vk_mapping[] = {
00125
00126 AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
00127
00128 AM('A', 'Z', 'A', 'Z'),
00129 AM('0', '9', '0', '9'),
00130
00131 AS(VK_ESCAPE, WKC_ESC),
00132 AS(VK_PAUSE, WKC_PAUSE),
00133 AS(VK_BACK, WKC_BACKSPACE),
00134 AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
00135
00136 AS(VK_SPACE, WKC_SPACE),
00137 AS(VK_RETURN, WKC_RETURN),
00138 AS(VK_TAB, WKC_TAB),
00139
00140
00141 AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
00142
00143
00144 AM(VK_NUMPAD0, VK_NUMPAD9, '0', '9'),
00145 AS(VK_DIVIDE, WKC_NUM_DIV),
00146 AS(VK_MULTIPLY, WKC_NUM_MUL),
00147 AS(VK_SUBTRACT, WKC_NUM_MINUS),
00148 AS(VK_ADD, WKC_NUM_PLUS),
00149 AS(VK_DECIMAL, WKC_NUM_DECIMAL),
00150
00151
00152 AS(0xBF, WKC_SLASH),
00153 AS(0xBA, WKC_SEMICOLON),
00154 AS(0xBB, WKC_EQUALS),
00155 AS(0xDB, WKC_L_BRACKET),
00156 AS(0xDC, WKC_BACKSLASH),
00157 AS(0xDD, WKC_R_BRACKET),
00158
00159 AS(0xDE, WKC_SINGLEQUOTE),
00160 AS(0xBC, WKC_COMMA),
00161 AS(0xBD, WKC_MINUS),
00162 AS(0xBE, WKC_PERIOD)
00163 };
00164
00165 static uint MapWindowsKey(uint sym)
00166 {
00167 const VkMapping *map;
00168 uint key = 0;
00169
00170 for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
00171 if ((uint)(sym - map->vk_from) <= map->vk_count) {
00172 key = sym - map->vk_from + map->map_to;
00173 break;
00174 }
00175 }
00176
00177 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
00178 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
00179 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
00180 return key;
00181 }
00182
00183 static bool AllocateDibSection(int w, int h, bool force = false);
00184
00185 static void ClientSizeChanged(int w, int h)
00186 {
00187
00188 if (AllocateDibSection(w, h)) {
00189
00190 _cur_palette.first_dirty = 0;
00191 _cur_palette.count_dirty = 256;
00192 _local_palette = _cur_palette;
00193
00194 BlitterFactory::GetCurrentBlitter()->PostResize();
00195
00196 GameSizeChanged();
00197 }
00198 }
00199
00200 #ifdef _DEBUG
00201
00202
00203 int RedrawScreenDebug()
00204 {
00205 HDC dc, dc2;
00206 static int _fooctr;
00207 HBITMAP old_bmp;
00208 HPALETTE old_palette;
00209
00210 UpdateWindows();
00211
00212 dc = GetDC(_wnd.main_wnd);
00213 dc2 = CreateCompatibleDC(dc);
00214
00215 old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
00216 old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
00217 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
00218 SelectPalette(dc, old_palette, TRUE);
00219 SelectObject(dc2, old_bmp);
00220 DeleteDC(dc2);
00221 ReleaseDC(_wnd.main_wnd, dc);
00222
00223 return _fooctr++;
00224 }
00225 #endif
00226
00227
00228 #if !defined(WM_MOUSELEAVE)
00229 #define WM_MOUSELEAVE 0x02A3
00230 #endif
00231 #define TID_POLLMOUSE 1
00232 #define MOUSE_POLL_DELAY 75
00233
00234 static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time)
00235 {
00236 RECT rc;
00237 POINT pt;
00238
00239
00240
00241
00242 GetClientRect(hwnd, &rc);
00243 MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2);
00244 GetCursorPos(&pt);
00245
00246 if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) {
00247 KillTimer(hwnd, event);
00248 PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L);
00249 }
00250 }
00251
00257 bool VideoDriver_Win32::MakeWindow(bool full_screen)
00258 {
00259 _fullscreen = full_screen;
00260
00261
00262 if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) {
00263 DestroyWindow(_wnd.main_wnd);
00264 _wnd.main_wnd = 0;
00265 }
00266
00267 #if defined(WINCE)
00268
00269 #else
00270 if (full_screen) {
00271 DEVMODE settings;
00272
00273
00274 if (_fullscreen_bpp < BlitterFactory::GetCurrentBlitter()->GetScreenDepth()) _fullscreen_bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
00275
00276 memset(&settings, 0, sizeof(settings));
00277 settings.dmSize = sizeof(settings);
00278 settings.dmFields =
00279 (_fullscreen_bpp != 0 ? DM_BITSPERPEL : 0) |
00280 DM_PELSWIDTH |
00281 DM_PELSHEIGHT |
00282 (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0);
00283 settings.dmBitsPerPel = _fullscreen_bpp;
00284 settings.dmPelsWidth = _wnd.width_org;
00285 settings.dmPelsHeight = _wnd.height_org;
00286 settings.dmDisplayFrequency = _display_hz;
00287
00288
00289 if (settings.dmBitsPerPel != 32 && ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
00290 settings.dmBitsPerPel = 32;
00291 }
00292
00293
00294 if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
00295 RECT r;
00296 GetWindowRect(GetDesktopWindow(), &r);
00297
00298
00299 if ((int)settings.dmPelsWidth != r.right - r.left || (int)settings.dmPelsHeight != r.bottom - r.top) {
00300 return this->ChangeResolution(r.right - r.left, r.bottom - r.top);
00301 }
00302 }
00303
00304 if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
00305 this->MakeWindow(false);
00306 return false;
00307 }
00308 } else if (_wnd.fullscreen) {
00309
00310 ChangeDisplaySettings(NULL, 0);
00311
00312 _wnd.width = _bck_resolution.width;
00313 _wnd.height = _bck_resolution.height;
00314 }
00315 #endif
00316
00317 {
00318 RECT r;
00319 DWORD style, showstyle;
00320 int w, h;
00321
00322 showstyle = SW_SHOWNORMAL;
00323 _wnd.fullscreen = full_screen;
00324 if (_wnd.fullscreen) {
00325 style = WS_POPUP;
00326 SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org);
00327 } else {
00328 style = WS_OVERLAPPEDWINDOW;
00329
00330 if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
00331 SetRect(&r, 0, 0, _wnd.width, _wnd.height);
00332 }
00333
00334 #if !defined(WINCE)
00335 AdjustWindowRect(&r, style, FALSE);
00336 #endif
00337 w = r.right - r.left;
00338 h = r.bottom - r.top;
00339
00340 if (_wnd.main_wnd != NULL) {
00341 if (!_window_maximize) SetWindowPos(_wnd.main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
00342 } else {
00343 TCHAR Windowtitle[50];
00344 int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
00345 int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
00346
00347 _sntprintf(Windowtitle, lengthof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision));
00348
00349 _wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0);
00350 if (_wnd.main_wnd == NULL) usererror("CreateWindow failed");
00351 ShowWindow(_wnd.main_wnd, showstyle);
00352 }
00353 }
00354
00355 BlitterFactory::GetCurrentBlitter()->PostResize();
00356
00357 GameSizeChanged();
00358 return true;
00359 }
00360
00362 static void PaintWindow(HDC dc)
00363 {
00364 HDC dc2 = CreateCompatibleDC(dc);
00365 HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
00366 HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
00367
00368 if (_cur_palette.count_dirty != 0) {
00369 Blitter *blitter = BlitterFactory::GetCurrentBlitter();
00370
00371 switch (blitter->UsePaletteAnimation()) {
00372 case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
00373 UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
00374 break;
00375
00376 case Blitter::PALETTE_ANIMATION_BLITTER:
00377 blitter->PaletteAnimate(_local_palette);
00378 break;
00379
00380 case Blitter::PALETTE_ANIMATION_NONE:
00381 break;
00382
00383 default:
00384 NOT_REACHED();
00385 }
00386 _cur_palette.count_dirty = 0;
00387 }
00388
00389 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
00390 SelectPalette(dc, old_palette, TRUE);
00391 SelectObject(dc2, old_bmp);
00392 DeleteDC(dc2);
00393 }
00394
00395 static void PaintWindowThread(void *)
00396 {
00397
00398 _draw_mutex->BeginCritical();
00399 SetEvent(_draw_thread_initialized);
00400
00401
00402 _draw_mutex->WaitForSignal();
00403
00404 while (_draw_continue) {
00405
00406 POINT pt = {0, 0};
00407 ClientToScreen(_wnd.main_wnd, &pt);
00408 OffsetRect(&_wnd.update_rect, pt.x, pt.y);
00409
00410
00411
00412 HRGN rgn = CreateRectRgnIndirect(&_wnd.update_rect);
00413 HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
00414
00415 PaintWindow(dc);
00416
00417
00418 SetRectEmpty(&_wnd.update_rect);
00419 ReleaseDC(_wnd.main_wnd, dc);
00420
00421
00422 GdiFlush();
00423
00424 _draw_mutex->WaitForSignal();
00425 }
00426
00427 _draw_mutex->EndCritical();
00428 _draw_thread->Exit();
00429 }
00430
00432 static LRESULT HandleCharMsg(uint keycode, WChar charcode)
00433 {
00434 #if !defined(UNICODE)
00435 static char prev_char = 0;
00436
00437 char input[2] = {(char)charcode, 0};
00438 int input_len = 1;
00439
00440 if (prev_char != 0) {
00441
00442 input[0] = prev_char;
00443 input[1] = (char)charcode;
00444 input_len = 2;
00445 } else if (IsDBCSLeadByte(charcode)) {
00446
00447 prev_char = charcode;
00448 return 0;
00449 }
00450 prev_char = 0;
00451
00452 wchar_t w[2];
00453 int len = MultiByteToWideChar(CP_ACP, 0, input, input_len, w, 2);
00454 switch (len) {
00455 case 1:
00456 charcode = w[0];
00457 break;
00458
00459 case 2:
00460 charcode = Utf16DecodeSurrogate(w[0], w[1]);
00461 break;
00462
00463 default:
00464 DEBUG(driver, 1, "Invalid DBCS character sequence encountered, dropping input");
00465 charcode = 0;
00466 break;
00467 }
00468 #else
00469 static WChar prev_char = 0;
00470
00471
00472 if (Utf16IsLeadSurrogate(charcode)) {
00473 if (prev_char != 0) DEBUG(driver, 1, "Got two UTF-16 lead surrogates, dropping the first one");
00474 prev_char = charcode;
00475 return 0;
00476 }
00477
00478
00479 if (prev_char != 0) {
00480 if (Utf16IsTrailSurrogate(charcode)) {
00481 charcode = Utf16DecodeSurrogate(prev_char, charcode);
00482 } else {
00483 DEBUG(driver, 1, "Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
00484 }
00485 }
00486 prev_char = 0;
00487 #endif
00488
00489 HandleKeypress(keycode, charcode);
00490
00491 return 0;
00492 }
00493
00494 #if !defined(WINCE) || _WIN32_WCE >= 0x400
00495
00496 static bool DrawIMECompositionString()
00497 {
00498 return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
00499 }
00500
00502 static void SetCompositionPos(HWND hwnd)
00503 {
00504 HIMC hIMC = ImmGetContext(hwnd);
00505 if (hIMC != NULL) {
00506 COMPOSITIONFORM cf;
00507 cf.dwStyle = CFS_POINT;
00508
00509 if (EditBoxInGlobalFocus()) {
00510
00511 Point pt = _focused_window->GetCaretPosition();
00512 cf.ptCurrentPos.x = _focused_window->left + pt.x;
00513 cf.ptCurrentPos.y = _focused_window->top + pt.y;
00514 } else {
00515 cf.ptCurrentPos.x = 0;
00516 cf.ptCurrentPos.y = 0;
00517 }
00518 ImmSetCompositionWindow(hIMC, &cf);
00519 }
00520 ImmReleaseContext(hwnd, hIMC);
00521 }
00522
00524 static void SetCandidatePos(HWND hwnd)
00525 {
00526 HIMC hIMC = ImmGetContext(hwnd);
00527 if (hIMC != NULL) {
00528 CANDIDATEFORM cf;
00529 cf.dwIndex = 0;
00530 cf.dwStyle = CFS_EXCLUDE;
00531
00532 if (EditBoxInGlobalFocus()) {
00533 Point pt = _focused_window->GetCaretPosition();
00534 cf.ptCurrentPos.x = _focused_window->left + pt.x;
00535 cf.ptCurrentPos.y = _focused_window->top + pt.y;
00536 if (_focused_window->window_class == WC_CONSOLE) {
00537 cf.rcArea.left = _focused_window->left;
00538 cf.rcArea.top = _focused_window->top;
00539 cf.rcArea.right = _focused_window->left + _focused_window->width;
00540 cf.rcArea.bottom = _focused_window->top + _focused_window->height;
00541 } else {
00542 cf.rcArea.left = _focused_window->left + _focused_window->nested_focus->pos_x;
00543 cf.rcArea.top = _focused_window->top + _focused_window->nested_focus->pos_y;
00544 cf.rcArea.right = cf.rcArea.left + _focused_window->nested_focus->current_x;
00545 cf.rcArea.bottom = cf.rcArea.top + _focused_window->nested_focus->current_y;
00546 }
00547 } else {
00548 cf.ptCurrentPos.x = 0;
00549 cf.ptCurrentPos.y = 0;
00550 SetRectEmpty(&cf.rcArea);
00551 }
00552 ImmSetCandidateWindow(hIMC, &cf);
00553 }
00554 ImmReleaseContext(hwnd, hIMC);
00555 }
00556
00558 static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam)
00559 {
00560 HIMC hIMC = ImmGetContext(hwnd);
00561
00562 if (hIMC != NULL) {
00563 if (lParam & GCS_RESULTSTR) {
00564
00565 LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
00566 TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR));
00567 len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str, len);
00568 str[len / sizeof(TCHAR)] = '\0';
00569
00570
00571 if (len > 0) {
00572 HandleTextInput(NULL, true);
00573 HandleTextInput(FS2OTTD(str));
00574 }
00575 SetCompositionPos(hwnd);
00576
00577
00578 lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
00579 }
00580
00581 if ((lParam & GCS_COMPSTR) && DrawIMECompositionString()) {
00582
00583 LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0);
00584 TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR));
00585 len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str, len);
00586 str[len / sizeof(TCHAR)] = '\0';
00587
00588 if (len > 0) {
00589 static char utf8_buf[1024];
00590 convert_from_fs(str, utf8_buf, lengthof(utf8_buf));
00591
00592
00593 LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0);
00594 const char *caret = utf8_buf;
00595 for (const TCHAR *c = str; *c != '\0' && *caret != '\0' && caret_bytes > 0; c++, caret_bytes--) {
00596
00597 #ifdef UNICODE
00598 if (Utf16IsLeadSurrogate(*c)) {
00599 #else
00600 if (IsDBCSLeadByte(*c)) {
00601 #endif
00602 c++;
00603 caret_bytes--;
00604 }
00605 Utf8Consume(&caret);
00606 }
00607
00608 HandleTextInput(utf8_buf, true, caret);
00609 } else {
00610 HandleTextInput(NULL, true);
00611 }
00612
00613 lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
00614 }
00615 }
00616 ImmReleaseContext(hwnd, hIMC);
00617
00618 return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
00619 }
00620
00622 static void CancelIMEComposition(HWND hwnd)
00623 {
00624 HIMC hIMC = ImmGetContext(hwnd);
00625 if (hIMC != NULL) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
00626 ImmReleaseContext(hwnd, hIMC);
00627
00628 HandleTextInput(NULL, true);
00629 }
00630
00631 #else
00632
00633 static bool DrawIMECompositionString() { return false; }
00634 static void SetCompositionPos(HWND hwnd) {}
00635 static void SetCandidatePos(HWND hwnd) {}
00636 static void CancelIMEComposition(HWND hwnd) {}
00637
00638 #endif
00639
00640 static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
00641 {
00642 static uint32 keycode = 0;
00643 static bool console = false;
00644 static bool in_sizemove = false;
00645
00646 switch (msg) {
00647 case WM_CREATE:
00648 SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
00649 SetCompositionPos(hwnd);
00650 #if !defined(WINCE) || _WIN32_WCE >= 0x400
00651 _imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
00652 #endif
00653 break;
00654
00655 case WM_ENTERSIZEMOVE:
00656 in_sizemove = true;
00657 break;
00658
00659 case WM_EXITSIZEMOVE:
00660 in_sizemove = false;
00661 break;
00662
00663 case WM_PAINT:
00664 if (!in_sizemove && _draw_mutex != NULL && !HasModalProgress()) {
00665
00666 RECT r;
00667 GetUpdateRect(hwnd, &r, FALSE);
00668 UnionRect(&_wnd.update_rect, &_wnd.update_rect, &r);
00669
00670
00671 ValidateRect(hwnd, NULL);
00672 _draw_mutex->SendSignal();
00673 } else {
00674 PAINTSTRUCT ps;
00675
00676 BeginPaint(hwnd, &ps);
00677 PaintWindow(ps.hdc);
00678 EndPaint(hwnd, &ps);
00679 }
00680 return 0;
00681
00682 case WM_PALETTECHANGED:
00683 if ((HWND)wParam == hwnd) return 0;
00684
00685
00686 case WM_QUERYNEWPALETTE: {
00687 HDC hDC = GetWindowDC(hwnd);
00688 HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
00689 UINT nChanged = RealizePalette(hDC);
00690
00691 SelectPalette(hDC, hOldPalette, TRUE);
00692 ReleaseDC(hwnd, hDC);
00693 if (nChanged != 0) InvalidateRect(hwnd, NULL, FALSE);
00694 return 0;
00695 }
00696
00697 case WM_CLOSE:
00698 HandleExitGameRequest();
00699 return 0;
00700
00701 case WM_DESTROY:
00702 if (_window_maximize) _cur_resolution = _bck_resolution;
00703 return 0;
00704
00705 case WM_LBUTTONDOWN:
00706 SetCapture(hwnd);
00707 _left_button_down = true;
00708 HandleMouseEvents();
00709 return 0;
00710
00711 case WM_LBUTTONUP:
00712 ReleaseCapture();
00713 _left_button_down = false;
00714 _left_button_clicked = false;
00715 HandleMouseEvents();
00716 return 0;
00717
00718 case WM_RBUTTONDOWN:
00719 SetCapture(hwnd);
00720 _right_button_down = true;
00721 _right_button_clicked = true;
00722 HandleMouseEvents();
00723 return 0;
00724
00725 case WM_RBUTTONUP:
00726 ReleaseCapture();
00727 _right_button_down = false;
00728 HandleMouseEvents();
00729 return 0;
00730
00731 case WM_MOUSELEAVE:
00732 UndrawMouseCursor();
00733 _cursor.in_window = false;
00734
00735 if (!_left_button_down && !_right_button_down) MyShowCursor(true);
00736 return 0;
00737
00738 case WM_MOUSEMOVE: {
00739 int x = (int16)LOWORD(lParam);
00740 int y = (int16)HIWORD(lParam);
00741 POINT pt;
00742
00743
00744
00745
00746 if (!_cursor.in_window) {
00747 _cursor.in_window = true;
00748 SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
00749 }
00750
00751 if (_cursor.fix_at) {
00752 int dx = x - _cursor.pos.x;
00753 int dy = y - _cursor.pos.y;
00754 if (dx != 0 || dy != 0) {
00755 _cursor.delta.x = dx;
00756 _cursor.delta.y = dy;
00757
00758 pt.x = _cursor.pos.x;
00759 pt.y = _cursor.pos.y;
00760
00761 ClientToScreen(hwnd, &pt);
00762 SetCursorPos(pt.x, pt.y);
00763 }
00764 } else {
00765 _cursor.delta.x = x - _cursor.pos.x;
00766 _cursor.delta.y = y - _cursor.pos.y;
00767 _cursor.pos.x = x;
00768 _cursor.pos.y = y;
00769 _cursor.dirty = true;
00770 }
00771 MyShowCursor(false);
00772 HandleMouseEvents();
00773 return 0;
00774 }
00775
00776 #if !defined(WINCE) || _WIN32_WCE >= 0x400
00777 case WM_INPUTLANGCHANGE:
00778 _imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
00779 break;
00780
00781 case WM_IME_SETCONTEXT:
00782
00783 if (DrawIMECompositionString()) lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
00784 break;
00785
00786 case WM_IME_STARTCOMPOSITION:
00787 SetCompositionPos(hwnd);
00788 if (DrawIMECompositionString()) return 0;
00789 break;
00790
00791 case WM_IME_COMPOSITION:
00792 return HandleIMEComposition(hwnd, wParam, lParam);
00793
00794 case WM_IME_ENDCOMPOSITION:
00795
00796 HandleTextInput(NULL, true);
00797 if (DrawIMECompositionString()) return 0;
00798 break;
00799
00800 case WM_IME_NOTIFY:
00801 if (wParam == IMN_OPENCANDIDATE) SetCandidatePos(hwnd);
00802 break;
00803
00804 #if !defined(UNICODE)
00805 case WM_IME_CHAR:
00806 if (GB(wParam, 8, 8) != 0) {
00807
00808 HandleCharMsg(0, GB(wParam, 8, 8));
00809 }
00810 HandleCharMsg(0, GB(wParam, 0, 8));
00811 return 0;
00812 #endif
00813 #endif
00814
00815 case WM_DEADCHAR:
00816 console = GB(lParam, 16, 8) == 41;
00817 return 0;
00818
00819 case WM_CHAR: {
00820 uint scancode = GB(lParam, 16, 8);
00821 uint charcode = wParam;
00822
00823
00824
00825 if (console && scancode == 41) {
00826 console = false;
00827 return 0;
00828 }
00829
00830
00831
00832 uint cur_keycode = keycode;
00833 keycode = 0;
00834
00835 return HandleCharMsg(cur_keycode, charcode);
00836 }
00837
00838 case WM_KEYDOWN: {
00839
00840 uint scancode = GB(lParam, 16, 8);
00841 keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
00842
00843
00844 MSG msg;
00845 if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
00846 if ((msg.message == WM_CHAR || msg.message == WM_DEADCHAR) && GB(lParam, 16, 8) == GB(msg.lParam, 16, 8)) {
00847 return 0;
00848 }
00849 }
00850
00851 uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
00852
00853
00854 if (charcode == 0) {
00855 HandleKeypress(keycode, 0);
00856 return 0;
00857 }
00858
00859
00860 if (HasBit(charcode, 31) && !console) {
00861 if (scancode == 41) {
00862 console = true;
00863 return 0;
00864 }
00865 }
00866 console = false;
00867
00868
00869
00870 uint cur_keycode = keycode;
00871 keycode = 0;
00872
00873 return HandleCharMsg(cur_keycode, LOWORD(charcode));
00874 }
00875
00876 case WM_SYSKEYDOWN:
00877 switch (wParam) {
00878 case VK_RETURN:
00879 case 'F':
00880 ToggleFullScreen(!_wnd.fullscreen);
00881 return 0;
00882
00883 case VK_MENU:
00884 return 0;
00885
00886 case VK_F10:
00887 HandleKeypress(MapWindowsKey(wParam), 0);
00888 return 0;
00889
00890 default:
00891 HandleKeypress(MapWindowsKey(wParam), 0);
00892 break;
00893 }
00894 break;
00895
00896 case WM_SIZE:
00897 if (wParam != SIZE_MINIMIZED) {
00898
00899
00900 _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
00901 if (_window_maximize || _fullscreen) _bck_resolution = _cur_resolution;
00902 ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
00903 }
00904 return 0;
00905
00906 #if !defined(WINCE)
00907 case WM_SIZING: {
00908 RECT *r = (RECT*)lParam;
00909 RECT r2;
00910 int w, h;
00911
00912 SetRect(&r2, 0, 0, 0, 0);
00913 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
00914
00915 w = r->right - r->left - (r2.right - r2.left);
00916 h = r->bottom - r->top - (r2.bottom - r2.top);
00917 w = max(w, 64);
00918 h = max(h, 64);
00919 SetRect(&r2, 0, 0, w, h);
00920
00921 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
00922 w = r2.right - r2.left;
00923 h = r2.bottom - r2.top;
00924
00925 switch (wParam) {
00926 case WMSZ_BOTTOM:
00927 r->bottom = r->top + h;
00928 break;
00929
00930 case WMSZ_BOTTOMLEFT:
00931 r->bottom = r->top + h;
00932 r->left = r->right - w;
00933 break;
00934
00935 case WMSZ_BOTTOMRIGHT:
00936 r->bottom = r->top + h;
00937 r->right = r->left + w;
00938 break;
00939
00940 case WMSZ_LEFT:
00941 r->left = r->right - w;
00942 break;
00943
00944 case WMSZ_RIGHT:
00945 r->right = r->left + w;
00946 break;
00947
00948 case WMSZ_TOP:
00949 r->top = r->bottom - h;
00950 break;
00951
00952 case WMSZ_TOPLEFT:
00953 r->top = r->bottom - h;
00954 r->left = r->right - w;
00955 break;
00956
00957 case WMSZ_TOPRIGHT:
00958 r->top = r->bottom - h;
00959 r->right = r->left + w;
00960 break;
00961 }
00962 return TRUE;
00963 }
00964 #endif
00965
00966
00967 #if !defined(WM_MOUSEWHEEL)
00968 # define WM_MOUSEWHEEL 0x020A
00969 #endif
00970 #if !defined(GET_WHEEL_DELTA_WPARAM)
00971 # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
00972 #endif
00973
00974 case WM_MOUSEWHEEL: {
00975 int delta = GET_WHEEL_DELTA_WPARAM(wParam);
00976
00977 if (delta < 0) {
00978 _cursor.wheel++;
00979 } else if (delta > 0) {
00980 _cursor.wheel--;
00981 }
00982 HandleMouseEvents();
00983 return 0;
00984 }
00985
00986 case WM_SETFOCUS:
00987 _wnd.has_focus = true;
00988 SetCompositionPos(hwnd);
00989 break;
00990
00991 case WM_KILLFOCUS:
00992 _wnd.has_focus = false;
00993 break;
00994
00995 #if !defined(WINCE)
00996 case WM_ACTIVATE: {
00997
00998 if (_exit_game) break;
00999
01000 bool active = (LOWORD(wParam) != WA_INACTIVE);
01001 bool minimized = (HIWORD(wParam) != 0);
01002 if (_wnd.fullscreen) {
01003 if (active && minimized) {
01004
01005 ShowWindow(hwnd, SW_RESTORE);
01006 static_cast<VideoDriver_Win32 *>(_video_driver)->MakeWindow(true);
01007 } else if (!active && !minimized) {
01008
01009 ShowWindow(hwnd, SW_MINIMIZE);
01010 ChangeDisplaySettings(NULL, 0);
01011 }
01012 }
01013 break;
01014 }
01015 #endif
01016 }
01017
01018 return DefWindowProc(hwnd, msg, wParam, lParam);
01019 }
01020
01021 static void RegisterWndClass()
01022 {
01023 static bool registered = false;
01024
01025 if (!registered) {
01026 HINSTANCE hinst = GetModuleHandle(NULL);
01027 WNDCLASS wnd = {
01028 CS_OWNDC,
01029 WndProcGdi,
01030 0,
01031 0,
01032 hinst,
01033 LoadIcon(hinst, MAKEINTRESOURCE(100)),
01034 LoadCursor(NULL, IDC_ARROW),
01035 0,
01036 0,
01037 _T("OTTD")
01038 };
01039
01040 registered = true;
01041 if (!RegisterClass(&wnd)) usererror("RegisterClass failed");
01042 }
01043 }
01044
01045 static bool AllocateDibSection(int w, int h, bool force)
01046 {
01047 BITMAPINFO *bi;
01048 HDC dc;
01049 int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
01050
01051 w = max(w, 64);
01052 h = max(h, 64);
01053
01054 if (bpp == 0) usererror("Can't use a blitter that blits 0 bpp for normal visuals");
01055
01056 if (!force && w == _screen.width && h == _screen.height) return false;
01057
01058 bi = (BITMAPINFO*)alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);
01059 memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);
01060 bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
01061
01062 bi->bmiHeader.biWidth = _wnd.width = w;
01063 bi->bmiHeader.biHeight = -(_wnd.height = h);
01064
01065 bi->bmiHeader.biPlanes = 1;
01066 bi->bmiHeader.biBitCount = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
01067 bi->bmiHeader.biCompression = BI_RGB;
01068
01069 if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
01070
01071 dc = GetDC(0);
01072 _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.buffer_bits, NULL, 0);
01073 if (_wnd.dib_sect == NULL) usererror("CreateDIBSection failed");
01074 ReleaseDC(0, dc);
01075
01076 _screen.width = w;
01077 _screen.pitch = (bpp == 8) ? Align(w, 4) : w;
01078 _screen.height = h;
01079 _screen.dst_ptr = _wnd.buffer_bits;
01080
01081 return true;
01082 }
01083
01084 static const Dimension default_resolutions[] = {
01085 { 640, 480 },
01086 { 800, 600 },
01087 { 1024, 768 },
01088 { 1152, 864 },
01089 { 1280, 800 },
01090 { 1280, 960 },
01091 { 1280, 1024 },
01092 { 1400, 1050 },
01093 { 1600, 1200 },
01094 { 1680, 1050 },
01095 { 1920, 1200 }
01096 };
01097
01098 static void FindResolutions()
01099 {
01100 uint n = 0;
01101 #if defined(WINCE)
01102
01103
01104 #else
01105 uint i;
01106 DEVMODEA dm;
01107
01108
01109
01110
01111 for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) {
01112 if (dm.dmBitsPerPel == BlitterFactory::GetCurrentBlitter()->GetScreenDepth() &&
01113 dm.dmPelsWidth >= 640 && dm.dmPelsHeight >= 480) {
01114 uint j;
01115
01116 for (j = 0; j < n; j++) {
01117 if (_resolutions[j].width == dm.dmPelsWidth && _resolutions[j].height == dm.dmPelsHeight) break;
01118 }
01119
01120
01121
01122
01123
01124 if (j == n) {
01125 _resolutions[j].width = dm.dmPelsWidth;
01126 _resolutions[j].height = dm.dmPelsHeight;
01127 if (++n == lengthof(_resolutions)) break;
01128 }
01129 }
01130 }
01131 #endif
01132
01133
01134 if (n == 0) {
01135 memcpy(_resolutions, default_resolutions, sizeof(default_resolutions));
01136 n = lengthof(default_resolutions);
01137 }
01138
01139 _num_resolutions = n;
01140 SortResolutions(_num_resolutions);
01141 }
01142
01143 static FVideoDriver_Win32 iFVideoDriver_Win32;
01144
01145 const char *VideoDriver_Win32::Start(const char * const *parm)
01146 {
01147 memset(&_wnd, 0, sizeof(_wnd));
01148
01149 RegisterWndClass();
01150
01151 MakePalette();
01152
01153 FindResolutions();
01154
01155 DEBUG(driver, 2, "Resolution for display: %ux%u", _cur_resolution.width, _cur_resolution.height);
01156
01157
01158 _wnd.width_org = _cur_resolution.width;
01159 _wnd.height_org = _cur_resolution.height;
01160
01161 AllocateDibSection(_cur_resolution.width, _cur_resolution.height);
01162 this->MakeWindow(_fullscreen);
01163
01164 MarkWholeScreenDirty();
01165
01166 _draw_threaded = GetDriverParam(parm, "no_threads") == NULL && GetDriverParam(parm, "no_thread") == NULL && GetCPUCoreCount() > 1;
01167
01168 return NULL;
01169 }
01170
01171 void VideoDriver_Win32::Stop()
01172 {
01173 DeleteObject(_wnd.gdi_palette);
01174 DeleteObject(_wnd.dib_sect);
01175 DestroyWindow(_wnd.main_wnd);
01176
01177 #if !defined(WINCE)
01178 if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0);
01179 #endif
01180 MyShowCursor(true);
01181 }
01182
01183 void VideoDriver_Win32::MakeDirty(int left, int top, int width, int height)
01184 {
01185 RECT r = { left, top, left + width, top + height };
01186
01187 InvalidateRect(_wnd.main_wnd, &r, FALSE);
01188 }
01189
01190 static void CheckPaletteAnim()
01191 {
01192 if (_cur_palette.count_dirty == 0) return;
01193
01194 _local_palette = _cur_palette;
01195 InvalidateRect(_wnd.main_wnd, NULL, FALSE);
01196 }
01197
01198 void VideoDriver_Win32::MainLoop()
01199 {
01200 MSG mesg;
01201 uint32 cur_ticks = GetTickCount();
01202 uint32 last_cur_ticks = cur_ticks;
01203 uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
01204
01205 if (_draw_threaded) {
01206
01207
01208 _draw_mutex = ThreadMutex::New();
01209 _draw_thread_initialized = CreateEvent(NULL, FALSE, FALSE, NULL);
01210 if (_draw_mutex == NULL || _draw_thread_initialized == NULL) {
01211 _draw_threaded = false;
01212 } else {
01213 _draw_continue = true;
01214 _draw_threaded = ThreadObject::New(&PaintWindowThread, NULL, &_draw_thread);
01215
01216
01217 if (!_draw_threaded) {
01218 delete _draw_mutex;
01219 _draw_mutex = NULL;
01220 CloseHandle(_draw_thread_initialized);
01221 _draw_thread_initialized = NULL;
01222 } else {
01223 DEBUG(driver, 1, "Threaded drawing enabled");
01224
01225 WaitForSingleObject(_draw_thread_initialized, INFINITE);
01226 _draw_mutex->BeginCritical();
01227 }
01228 }
01229 }
01230
01231 _wnd.running = true;
01232
01233 CheckPaletteAnim();
01234 for (;;) {
01235 uint32 prev_cur_ticks = cur_ticks;
01236
01237 while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
01238 InteractiveRandom();
01239
01240 if (EditBoxInGlobalFocus()) TranslateMessage(&mesg);
01241 DispatchMessage(&mesg);
01242 }
01243 if (_exit_game) return;
01244
01245 #if defined(_DEBUG)
01246 if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 &&
01247 #else
01248
01249 if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 &&
01250 #endif
01251 !_networking && _game_mode != GM_MENU) {
01252 _fast_forward |= 2;
01253 } else if (_fast_forward & 2) {
01254 _fast_forward = 0;
01255 }
01256
01257 cur_ticks = GetTickCount();
01258 if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) {
01259 _realtime_tick += cur_ticks - last_cur_ticks;
01260 last_cur_ticks = cur_ticks;
01261 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
01262
01263 bool old_ctrl_pressed = _ctrl_pressed;
01264
01265 _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0;
01266 _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0;
01267
01268
01269 if (_wnd.has_focus) {
01270 _dirkeys =
01271 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
01272 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
01273 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
01274 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
01275 } else {
01276 _dirkeys = 0;
01277 }
01278
01279 if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
01280
01281 #if !defined(WINCE)
01282
01283 GdiFlush();
01284 #endif
01285
01286
01287
01288 if (_draw_threaded) _draw_mutex->EndCritical();
01289 GameLoop();
01290 if (_draw_threaded) _draw_mutex->BeginCritical();
01291
01292 if (_force_full_redraw) MarkWholeScreenDirty();
01293
01294 UpdateWindows();
01295 CheckPaletteAnim();
01296 } else {
01297 #if !defined(WINCE)
01298
01299 GdiFlush();
01300 #endif
01301
01302
01303 if (_draw_threaded) _draw_mutex->EndCritical();
01304 Sleep(1);
01305 if (_draw_threaded) _draw_mutex->BeginCritical();
01306
01307 NetworkDrawChatMessage();
01308 DrawMouseCursor();
01309 }
01310 }
01311
01312 if (_draw_threaded) {
01313 _draw_continue = false;
01314
01315
01316 _draw_mutex->SendSignal();
01317 _draw_mutex->EndCritical();
01318 _draw_thread->Join();
01319
01320 CloseHandle(_draw_thread_initialized);
01321 delete _draw_mutex;
01322 delete _draw_thread;
01323 }
01324 }
01325
01326 bool VideoDriver_Win32::ChangeResolution(int w, int h)
01327 {
01328 if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
01329 if (_window_maximize) ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL);
01330
01331 _wnd.width = _wnd.width_org = w;
01332 _wnd.height = _wnd.height_org = h;
01333
01334 bool ret = this->MakeWindow(_fullscreen);
01335 if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
01336 return ret;
01337 }
01338
01339 bool VideoDriver_Win32::ToggleFullscreen(bool full_screen)
01340 {
01341 if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
01342 bool ret = this->MakeWindow(full_screen);
01343 if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
01344 return ret;
01345 }
01346
01347 bool VideoDriver_Win32::AfterBlitterChange()
01348 {
01349 if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
01350 bool ret = AllocateDibSection(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen);
01351 if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
01352 return ret;
01353 }
01354
01355 void VideoDriver_Win32::EditBoxLostFocus()
01356 {
01357 if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
01358 CancelIMEComposition(_wnd.main_wnd);
01359 SetCompositionPos(_wnd.main_wnd);
01360 SetCandidatePos(_wnd.main_wnd);
01361 if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
01362 }