win32_v.cpp

Go to the documentation of this file.
00001 /* $Id: win32_v.cpp 26367 2014-02-23 16:08:50Z michi_cc $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
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 /* Missing define in MinGW headers. */
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   /* Pageup stuff + up/down */
00126   AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
00127   /* Map letters & digits */
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   /* Function keys */
00141   AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
00142 
00143   /* Numeric part */
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   /* Other non-letter keys */
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   /* allocate new dib section of the new size */
00188   if (AllocateDibSection(w, h)) {
00189     /* mark all palette colours dirty */
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 /* Keep this function here..
00202  * It allows you to redraw the screen from within the MSVC debugger */
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 /* Windows 95 will not have a WM_MOUSELEAVE message, so define it if needed */
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   /* Get the rectangle of our window and translate it to screen coordinates.
00240    * Compare this with the current screen coordinates of the mouse and if it
00241    * falls outside of the area or our window we have left the window. */
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   /* recreate window? */
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   /* WinCE is always fullscreen */
00269 #else
00270   if (full_screen) {
00271     DEVMODE settings;
00272 
00273     /* Make sure we are always at least the screen-depth of the blitter */
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     /* Check for 8 bpp support. */
00289     if (settings.dmBitsPerPel != 32 && ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
00290       settings.dmBitsPerPel = 32;
00291     }
00292 
00293     /* Test fullscreen with current resolution, if it fails use desktop resolution. */
00294     if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
00295       RECT r;
00296       GetWindowRect(GetDesktopWindow(), &r);
00297       /* Guard against recursion. If we already failed here once, just fall through to
00298        * the next ChangeDisplaySettings call which will fail and error out appropriately. */
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);  // don't care about the result
00306       return false;  // the request failed
00307     }
00308   } else if (_wnd.fullscreen) {
00309     /* restore display? */
00310     ChangeDisplaySettings(NULL, 0);
00311     /* restore the resolution */
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       /* On window creation, check if we were in maximize mode before */
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(); // invalidate all windows, force redraw
00358   return true; // the request succeeded
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   /* First tell the main thread we're started */
00398   _draw_mutex->BeginCritical();
00399   SetEvent(_draw_thread_initialized);
00400 
00401   /* Now wait for the first thing to draw! */
00402   _draw_mutex->WaitForSignal();
00403 
00404   while (_draw_continue) {
00405     /* Convert update region from logical to device coordinates. */
00406     POINT pt = {0, 0};
00407     ClientToScreen(_wnd.main_wnd, &pt);
00408     OffsetRect(&_wnd.update_rect, pt.x, pt.y);
00409 
00410     /* Create a device context that is clipped to the region we need to draw.
00411      * GetDCEx 'consumes' the update region, so we may not destroy it ourself. */
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     /* Clear update rect. */
00418     SetRectEmpty(&_wnd.update_rect);
00419     ReleaseDC(_wnd.main_wnd, dc);
00420 
00421     /* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
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     /* We stored a lead byte previously, combine it with this byte. */
00442     input[0] = prev_char;
00443     input[1] = (char)charcode;
00444     input_len = 2;
00445   } else if (IsDBCSLeadByte(charcode)) {
00446     /* We got a lead byte, store and exit. */
00447     prev_char = charcode;
00448     return 0;
00449   }
00450   prev_char = 0;
00451 
00452   wchar_t w[2]; // Can get up to two code points as a result.
00453   int len = MultiByteToWideChar(CP_ACP, 0, input, input_len, w, 2);
00454   switch (len) {
00455     case 1: // Normal unicode character.
00456       charcode = w[0];
00457       break;
00458 
00459     case 2: // Got an UTF-16 surrogate pair back.
00460       charcode = Utf16DecodeSurrogate(w[0], w[1]);
00461       break;
00462 
00463     default: // Some kind of error.
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   /* Did we get a lead surrogate? If yes, store and exit. */
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   /* Stored lead surrogate and incoming trail surrogate? Combine and forward to input handling. */
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 /* UNICODE */
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       /* Get caret position. */
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       /* Read result string from the IME. */
00565       LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); // Length is always in bytes, even in UNICODE build.
00566       TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR));
00567       len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str, len);
00568       str[len / sizeof(TCHAR)] = '\0';
00569 
00570       /* Transmit text to windowing system. */
00571       if (len > 0) {
00572         HandleTextInput(NULL, true); // Clear marked string.
00573         HandleTextInput(FS2OTTD(str));
00574       }
00575       SetCompositionPos(hwnd);
00576 
00577       /* Don't pass the result string on to the default window proc. */
00578       lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
00579     }
00580 
00581     if ((lParam & GCS_COMPSTR) && DrawIMECompositionString()) {
00582       /* Read composition string from the IME. */
00583       LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0); // Length is always in bytes, even in UNICODE build.
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         /* Convert caret position from bytes in the input string to a position in the UTF-8 encoded string. */
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           /* Skip DBCS lead bytes or leading surrogates. */
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   /* Clear any marked string from the current edit box. */
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 /* !defined(WINCE) || _WIN32_WCE >= 0x400 */
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         /* Get the union of the old update rect and the new update rect. */
00666         RECT r;
00667         GetUpdateRect(hwnd, &r, FALSE);
00668         UnionRect(&_wnd.update_rect, &_wnd.update_rect, &r);
00669 
00670         /* Mark the window as updated, otherwise Windows would send more WM_PAINT messages. */
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       /* FALL THROUGH */
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       /* If the mouse was not in the window and it has moved it means it has
00744        * come into the window, so start drawing the mouse. Also start
00745        * tracking the mouse for exiting the window */
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       /* Don't show the composition window if we draw the string ourself. */
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       /* Clear any pending composition string. */
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         /* DBCS character, send lead byte first. */
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       /* If the console key is a dead-key, we need to press it twice to get a WM_CHAR message.
00824        * But we then get two WM_CHAR messages, so ignore the first one */
00825       if (console && scancode == 41) {
00826         console = false;
00827         return 0;
00828       }
00829 
00830       /* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
00831        * clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */
00832       uint cur_keycode = keycode;
00833       keycode = 0;
00834 
00835       return HandleCharMsg(cur_keycode, charcode);
00836     }
00837 
00838     case WM_KEYDOWN: {
00839       /* No matter the keyboard layout, we will map the '~' to the console. */
00840       uint scancode = GB(lParam, 16, 8);
00841       keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
00842 
00843       /* Silently drop all messages handled by WM_CHAR. */
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       /* No character translation? */
00854       if (charcode == 0) {
00855         HandleKeypress(keycode, 0);
00856         return 0;
00857       }
00858 
00859       /* Is the console key a dead key? If yes, ignore the first key down event. */
00860       if (HasBit(charcode, 31) && !console) {
00861         if (scancode == 41) {
00862           console = true;
00863           return 0;
00864         }
00865       }
00866       console = false;
00867 
00868       /* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
00869        * clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */
00870       uint cur_keycode = keycode;
00871       keycode = 0;
00872 
00873       return HandleCharMsg(cur_keycode, LOWORD(charcode));
00874     }
00875 
00876     case WM_SYSKEYDOWN: // user presses F10 or Alt, both activating the title-menu
00877       switch (wParam) {
00878         case VK_RETURN:
00879         case 'F': // Full Screen on ALT + ENTER/F
00880           ToggleFullScreen(!_wnd.fullscreen);
00881           return 0;
00882 
00883         case VK_MENU: // Just ALT
00884           return 0; // do nothing
00885 
00886         case VK_F10: // F10, ignore activation of menu
00887           HandleKeypress(MapWindowsKey(wParam), 0);
00888           return 0;
00889 
00890         default: // ALT in combination with something else
00891           HandleKeypress(MapWindowsKey(wParam), 0);
00892           break;
00893       }
00894       break;
00895 
00896     case WM_SIZE:
00897       if (wParam != SIZE_MINIMIZED) {
00898         /* Set maximized flag when we maximize (obviously), but also when we
00899          * switched to fullscreen from a maximized state */
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 /* needed for wheel */
00967 #if !defined(WM_MOUSEWHEEL)
00968 # define WM_MOUSEWHEEL 0x020A
00969 #endif  /* WM_MOUSEWHEEL */
00970 #if !defined(GET_WHEEL_DELTA_WPARAM)
00971 # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
00972 #endif  /* GET_WHEEL_DELTA_WPARAM */
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       /* Don't do anything if we are closing openttd */
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           /* Restore the game window */
01005           ShowWindow(hwnd, SW_RESTORE);
01006           static_cast<VideoDriver_Win32 *>(_video_driver)->MakeWindow(true);
01007         } else if (!active && !minimized) {
01008           /* Minimise the window and restore desktop */
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   /* EnumDisplaySettingsW is only supported in CE 4.2+
01103    * XXX -- One might argue that we assume 4.2+ on every system. Then we can use this function safely */
01104 #else
01105   uint i;
01106   DEVMODEA dm;
01107 
01108   /* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95
01109    * Doesn't really matter since we don't pass a string anyways, but still
01110    * a letdown */
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       /* In the previous loop we have checked already existing/added resolutions if
01121        * they are the same as the new ones. If this is not the case (j == n); we have
01122        * looped all and found none, add the new one to the list. If we have reached the
01123        * maximum amount of resolutions, then quit querying the display */
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   /* We have found no resolutions, show the default list */
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   /* fullscreen uses those */
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     /* Initialise the mutex first, because that's the thing we *need*
01207      * directly in the newly created thread. */
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       /* Free the mutex if we won't be able to use it. */
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         /* Wait till the draw thread has started itself. */
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; // to check for wrapping
01236 
01237     while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
01238       InteractiveRandom(); // randomness
01239       /* Convert key messages to char messages if we want text input. */
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     /* Speed up using TAB, but disable for ALT+TAB of course */
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       /* determine which directional keys are down */
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       /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
01283       GdiFlush();
01284 #endif
01285 
01286       /* The game loop is the part that can run asynchronously.
01287        * The rest except sleeping can't. */
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       /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
01299       GdiFlush();
01300 #endif
01301 
01302       /* Release the thread while sleeping */
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     /* Sending signal if there is no thread blocked
01315      * is very valid and results in noop */
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); // _wnd.fullscreen screws up ingame resolution switching
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 }