win32_v.cpp

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