OpenTTD
win32_v.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "../openttd.h"
14 #include "../gfx_func.h"
15 #include "../os/windows/win32.h"
16 #include "../rev.h"
17 #include "../blitter/factory.hpp"
18 #include "../network/network.h"
19 #include "../core/math_func.hpp"
20 #include "../core/random_func.hpp"
21 #include "../texteff.hpp"
22 #include "../thread/thread.h"
23 #include "../progress.h"
24 #include "../window_gui.h"
25 #include "../window_func.h"
26 #include "../framerate_type.h"
27 #include "win32_v.h"
28 #include <windows.h>
29 #include <imm.h>
30 
31 #include "../safeguards.h"
32 
33 /* Missing define in MinGW headers. */
34 #ifndef MAPVK_VK_TO_CHAR
35 #define MAPVK_VK_TO_CHAR (2)
36 #endif
37 
38 #ifndef PM_QS_INPUT
39 #define PM_QS_INPUT 0x20000
40 #endif
41 
42 typedef BOOL (WINAPI *PFNTRACKMOUSEEVENT)(LPTRACKMOUSEEVENT lpEventTrack);
43 static PFNTRACKMOUSEEVENT _pTrackMouseEvent = NULL;
44 
45 static struct {
46  HWND main_wnd;
47  HBITMAP dib_sect;
48  void *buffer_bits;
49  HPALETTE gdi_palette;
50  RECT update_rect;
51  int width;
52  int height;
53  int width_org;
54  int height_org;
55  bool fullscreen;
56  bool has_focus;
57  bool running;
58 } _wnd;
59 
60 bool _force_full_redraw;
61 bool _window_maximize;
62 uint _display_hz;
63 static Dimension _bck_resolution;
64 DWORD _imm_props;
65 
67 static bool _draw_threaded;
69 static ThreadObject *_draw_thread = NULL;
71 static ThreadMutex *_draw_mutex = NULL;
73 static HANDLE _draw_thread_initialized = NULL;
75 static volatile bool _draw_continue;
78 
79 static void MakePalette()
80 {
81  LOGPALETTE *pal = (LOGPALETTE*)alloca(sizeof(LOGPALETTE) + (256 - 1) * sizeof(PALETTEENTRY));
82 
83  pal->palVersion = 0x300;
84  pal->palNumEntries = 256;
85 
86  for (uint i = 0; i != 256; i++) {
87  pal->palPalEntry[i].peRed = _cur_palette.palette[i].r;
88  pal->palPalEntry[i].peGreen = _cur_palette.palette[i].g;
89  pal->palPalEntry[i].peBlue = _cur_palette.palette[i].b;
90  pal->palPalEntry[i].peFlags = 0;
91 
92  }
93  _wnd.gdi_palette = CreatePalette(pal);
94  if (_wnd.gdi_palette == NULL) usererror("CreatePalette failed!\n");
95 
98  _local_palette = _cur_palette;
99 }
100 
101 static void UpdatePalette(HDC dc, uint start, uint count)
102 {
103  RGBQUAD rgb[256];
104  uint i;
105 
106  for (i = 0; i != count; i++) {
107  rgb[i].rgbRed = _local_palette.palette[start + i].r;
108  rgb[i].rgbGreen = _local_palette.palette[start + i].g;
109  rgb[i].rgbBlue = _local_palette.palette[start + i].b;
110  rgb[i].rgbReserved = 0;
111  }
112 
113  SetDIBColorTable(dc, start, count, rgb);
114 }
115 
116 bool VideoDriver_Win32::ClaimMousePointer()
117 {
118  MyShowCursor(false, true);
119  return true;
120 }
121 
122 struct VkMapping {
123  byte vk_from;
124  byte vk_count;
125  byte map_to;
126 };
127 
128 #define AS(x, z) {x, 0, z}
129 #define AM(x, y, z, w) {x, y - x, z}
130 
131 static const VkMapping _vk_mapping[] = {
132  /* Pageup stuff + up/down */
133  AM(VK_PRIOR, VK_DOWN, WKC_PAGEUP, WKC_DOWN),
134  /* Map letters & digits */
135  AM('A', 'Z', 'A', 'Z'),
136  AM('0', '9', '0', '9'),
137 
138  AS(VK_ESCAPE, WKC_ESC),
139  AS(VK_PAUSE, WKC_PAUSE),
140  AS(VK_BACK, WKC_BACKSPACE),
141  AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE),
142 
143  AS(VK_SPACE, WKC_SPACE),
144  AS(VK_RETURN, WKC_RETURN),
145  AS(VK_TAB, WKC_TAB),
146 
147  /* Function keys */
148  AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
149 
150  /* Numeric part */
151  AM(VK_NUMPAD0, VK_NUMPAD9, '0', '9'),
152  AS(VK_DIVIDE, WKC_NUM_DIV),
153  AS(VK_MULTIPLY, WKC_NUM_MUL),
154  AS(VK_SUBTRACT, WKC_NUM_MINUS),
155  AS(VK_ADD, WKC_NUM_PLUS),
156  AS(VK_DECIMAL, WKC_NUM_DECIMAL),
157 
158  /* Other non-letter keys */
159  AS(0xBF, WKC_SLASH),
160  AS(0xBA, WKC_SEMICOLON),
161  AS(0xBB, WKC_EQUALS),
162  AS(0xDB, WKC_L_BRACKET),
163  AS(0xDC, WKC_BACKSLASH),
164  AS(0xDD, WKC_R_BRACKET),
165 
166  AS(0xDE, WKC_SINGLEQUOTE),
167  AS(0xBC, WKC_COMMA),
168  AS(0xBD, WKC_MINUS),
169  AS(0xBE, WKC_PERIOD)
170 };
171 
172 static uint MapWindowsKey(uint sym)
173 {
174  const VkMapping *map;
175  uint key = 0;
176 
177  for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
178  if ((uint)(sym - map->vk_from) <= map->vk_count) {
179  key = sym - map->vk_from + map->map_to;
180  break;
181  }
182  }
183 
184  if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT;
185  if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
186  if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT;
187  return key;
188 }
189 
190 static bool AllocateDibSection(int w, int h, bool force = false);
191 
192 static void ClientSizeChanged(int w, int h)
193 {
194  /* allocate new dib section of the new size */
195  if (AllocateDibSection(w, h)) {
196  /* mark all palette colours dirty */
199  _local_palette = _cur_palette;
200 
202 
203  GameSizeChanged();
204  }
205 }
206 
207 #ifdef _DEBUG
208 /* Keep this function here..
209  * It allows you to redraw the screen from within the MSVC debugger */
210 int RedrawScreenDebug()
211 {
212  HDC dc, dc2;
213  static int _fooctr;
214  HBITMAP old_bmp;
215  HPALETTE old_palette;
216 
217  UpdateWindows();
218 
219  dc = GetDC(_wnd.main_wnd);
220  dc2 = CreateCompatibleDC(dc);
221 
222  old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
223  old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
224  BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
225  SelectPalette(dc, old_palette, TRUE);
226  SelectObject(dc2, old_bmp);
227  DeleteDC(dc2);
228  ReleaseDC(_wnd.main_wnd, dc);
229 
230  return _fooctr++;
231 }
232 #endif
233 
234 /* Windows 95 will not have a WM_MOUSELEAVE message, so define it if needed */
235 #if !defined(WM_MOUSELEAVE)
236 #define WM_MOUSELEAVE 0x02A3
237 #endif
238 #define TID_POLLMOUSE 1
239 #define MOUSE_POLL_DELAY 75
240 
241 static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT_PTR event, DWORD time)
242 {
243  RECT rc;
244  POINT pt;
245 
246  /* Get the rectangle of our window and translate it to screen coordinates.
247  * Compare this with the current screen coordinates of the mouse and if it
248  * falls outside of the area or our window we have left the window. */
249  GetClientRect(hwnd, &rc);
250  MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2);
251  GetCursorPos(&pt);
252 
253  if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) {
254  KillTimer(hwnd, event);
255  PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L);
256  }
257 }
258 
264 bool VideoDriver_Win32::MakeWindow(bool full_screen)
265 {
266  _fullscreen = full_screen;
267 
268  /* recreate window? */
269  if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) {
270  DestroyWindow(_wnd.main_wnd);
271  _wnd.main_wnd = 0;
272  }
273 
274  if (full_screen) {
275  DEVMODE settings;
276 
277  memset(&settings, 0, sizeof(settings));
278  settings.dmSize = sizeof(settings);
279  settings.dmFields =
280  DM_BITSPERPEL |
281  DM_PELSWIDTH |
282  DM_PELSHEIGHT |
283  (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0);
284  settings.dmBitsPerPel = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
285  settings.dmPelsWidth = _wnd.width_org;
286  settings.dmPelsHeight = _wnd.height_org;
287  settings.dmDisplayFrequency = _display_hz;
288 
289  /* Check for 8 bpp support. */
290  if (settings.dmBitsPerPel == 8 &&
291  (_support8bpp != S8BPP_HARDWARE || ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL)) {
292  settings.dmBitsPerPel = 32;
293  }
294 
295  /* Test fullscreen with current resolution, if it fails use desktop resolution. */
296  if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN | CDS_TEST) != DISP_CHANGE_SUCCESSFUL) {
297  RECT r;
298  GetWindowRect(GetDesktopWindow(), &r);
299  /* Guard against recursion. If we already failed here once, just fall through to
300  * the next ChangeDisplaySettings call which will fail and error out appropriately. */
301  if ((int)settings.dmPelsWidth != r.right - r.left || (int)settings.dmPelsHeight != r.bottom - r.top) {
302  return this->ChangeResolution(r.right - r.left, r.bottom - r.top);
303  }
304  }
305 
306  if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
307  this->MakeWindow(false); // don't care about the result
308  return false; // the request failed
309  }
310  } else if (_wnd.fullscreen) {
311  /* restore display? */
312  ChangeDisplaySettings(NULL, 0);
313  /* restore the resolution */
314  _wnd.width = _bck_resolution.width;
315  _wnd.height = _bck_resolution.height;
316  }
317 
318  {
319  RECT r;
320  DWORD style, showstyle;
321  int w, h;
322 
323  showstyle = SW_SHOWNORMAL;
324  _wnd.fullscreen = full_screen;
325  if (_wnd.fullscreen) {
326  style = WS_POPUP;
327  SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org);
328  } else {
329  style = WS_OVERLAPPEDWINDOW;
330  /* On window creation, check if we were in maximize mode before */
331  if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
332  SetRect(&r, 0, 0, _wnd.width, _wnd.height);
333  }
334 
335  AdjustWindowRect(&r, style, FALSE);
336  w = r.right - r.left;
337  h = r.bottom - r.top;
338 
339  if (_wnd.main_wnd != NULL) {
340  if (!_window_maximize) SetWindowPos(_wnd.main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
341  } else {
342  int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
343  int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
344 
345  char window_title[64];
346  seprintf(window_title, lastof(window_title), "OpenTTD %s", _openttd_revision);
347 
348  _wnd.main_wnd = CreateWindow(_T("OTTD"), MB_TO_WIDE(window_title), style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0);
349  if (_wnd.main_wnd == NULL) usererror("CreateWindow failed");
350  ShowWindow(_wnd.main_wnd, showstyle);
351  }
352  }
353 
355 
356  GameSizeChanged(); // invalidate all windows, force redraw
357  return true; // the request succeeded
358 }
359 
361 static void PaintWindow(HDC dc)
362 {
363  PerformanceMeasurer framerate(PFE_VIDEO);
364 
365  HDC dc2 = CreateCompatibleDC(dc);
366  HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
367  HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
368 
369  if (_cur_palette.count_dirty != 0) {
371 
372  switch (blitter->UsePaletteAnimation()) {
374  UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
375  break;
376 
378  blitter->PaletteAnimate(_local_palette);
379  break;
380 
382  break;
383 
384  default:
385  NOT_REACHED();
386  }
388  }
389 
390  BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
391  SelectPalette(dc, old_palette, TRUE);
392  SelectObject(dc2, old_bmp);
393  DeleteDC(dc2);
394 }
395 
396 static void PaintWindowThread(void *)
397 {
398  /* First tell the main thread we're started */
399  _draw_mutex->BeginCritical();
400  SetEvent(_draw_thread_initialized);
401 
402  /* Now wait for the first thing to draw! */
403  _draw_mutex->WaitForSignal();
404 
405  while (_draw_continue) {
406  /* Convert update region from logical to device coordinates. */
407  POINT pt = {0, 0};
408  ClientToScreen(_wnd.main_wnd, &pt);
409  OffsetRect(&_wnd.update_rect, pt.x, pt.y);
410 
411  /* Create a device context that is clipped to the region we need to draw.
412  * GetDCEx 'consumes' the update region, so we may not destroy it ourself. */
413  HRGN rgn = CreateRectRgnIndirect(&_wnd.update_rect);
414  HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
415 
416  PaintWindow(dc);
417 
418  /* Clear update rect. */
419  SetRectEmpty(&_wnd.update_rect);
420  ReleaseDC(_wnd.main_wnd, dc);
421 
422  /* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
423  GdiFlush();
424 
425  _draw_mutex->WaitForSignal();
426  }
427 
428  _draw_mutex->EndCritical();
429  _draw_thread->Exit();
430 }
431 
433 static LRESULT HandleCharMsg(uint keycode, WChar charcode)
434 {
435 #if !defined(UNICODE)
436  static char prev_char = 0;
437 
438  char input[2] = {(char)charcode, 0};
439  int input_len = 1;
440 
441  if (prev_char != 0) {
442  /* We stored a lead byte previously, combine it with this byte. */
443  input[0] = prev_char;
444  input[1] = (char)charcode;
445  input_len = 2;
446  } else if (IsDBCSLeadByte(charcode)) {
447  /* We got a lead byte, store and exit. */
448  prev_char = charcode;
449  return 0;
450  }
451  prev_char = 0;
452 
453  wchar_t w[2]; // Can get up to two code points as a result.
454  int len = MultiByteToWideChar(CP_ACP, 0, input, input_len, w, 2);
455  switch (len) {
456  case 1: // Normal unicode character.
457  charcode = w[0];
458  break;
459 
460  case 2: // Got an UTF-16 surrogate pair back.
461  charcode = Utf16DecodeSurrogate(w[0], w[1]);
462  break;
463 
464  default: // Some kind of error.
465  DEBUG(driver, 1, "Invalid DBCS character sequence encountered, dropping input");
466  charcode = 0;
467  break;
468  }
469 #else
470  static WChar prev_char = 0;
471 
472  /* Did we get a lead surrogate? If yes, store and exit. */
473  if (Utf16IsLeadSurrogate(charcode)) {
474  if (prev_char != 0) DEBUG(driver, 1, "Got two UTF-16 lead surrogates, dropping the first one");
475  prev_char = charcode;
476  return 0;
477  }
478 
479  /* Stored lead surrogate and incoming trail surrogate? Combine and forward to input handling. */
480  if (prev_char != 0) {
481  if (Utf16IsTrailSurrogate(charcode)) {
482  charcode = Utf16DecodeSurrogate(prev_char, charcode);
483  } else {
484  DEBUG(driver, 1, "Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate");
485  }
486  }
487  prev_char = 0;
488 #endif /* UNICODE */
489 
490  HandleKeypress(keycode, charcode);
491 
492  return 0;
493 }
494 
497 {
498  return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI);
499 }
500 
502 static void SetCompositionPos(HWND hwnd)
503 {
504  HIMC hIMC = ImmGetContext(hwnd);
505  if (hIMC != NULL) {
506  COMPOSITIONFORM cf;
507  cf.dwStyle = CFS_POINT;
508 
509  if (EditBoxInGlobalFocus()) {
510  /* Get caret position. */
511  Point pt = _focused_window->GetCaretPosition();
512  cf.ptCurrentPos.x = _focused_window->left + pt.x;
513  cf.ptCurrentPos.y = _focused_window->top + pt.y;
514  } else {
515  cf.ptCurrentPos.x = 0;
516  cf.ptCurrentPos.y = 0;
517  }
518  ImmSetCompositionWindow(hIMC, &cf);
519  }
520  ImmReleaseContext(hwnd, hIMC);
521 }
522 
524 static void SetCandidatePos(HWND hwnd)
525 {
526  HIMC hIMC = ImmGetContext(hwnd);
527  if (hIMC != NULL) {
528  CANDIDATEFORM cf;
529  cf.dwIndex = 0;
530  cf.dwStyle = CFS_EXCLUDE;
531 
532  if (EditBoxInGlobalFocus()) {
533  Point pt = _focused_window->GetCaretPosition();
534  cf.ptCurrentPos.x = _focused_window->left + pt.x;
535  cf.ptCurrentPos.y = _focused_window->top + pt.y;
536  if (_focused_window->window_class == WC_CONSOLE) {
537  cf.rcArea.left = _focused_window->left;
538  cf.rcArea.top = _focused_window->top;
539  cf.rcArea.right = _focused_window->left + _focused_window->width;
540  cf.rcArea.bottom = _focused_window->top + _focused_window->height;
541  } else {
542  cf.rcArea.left = _focused_window->left + _focused_window->nested_focus->pos_x;
543  cf.rcArea.top = _focused_window->top + _focused_window->nested_focus->pos_y;
544  cf.rcArea.right = cf.rcArea.left + _focused_window->nested_focus->current_x;
545  cf.rcArea.bottom = cf.rcArea.top + _focused_window->nested_focus->current_y;
546  }
547  } else {
548  cf.ptCurrentPos.x = 0;
549  cf.ptCurrentPos.y = 0;
550  SetRectEmpty(&cf.rcArea);
551  }
552  ImmSetCandidateWindow(hIMC, &cf);
553  }
554  ImmReleaseContext(hwnd, hIMC);
555 }
556 
558 static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam)
559 {
560  HIMC hIMC = ImmGetContext(hwnd);
561 
562  if (hIMC != NULL) {
563  if (lParam & GCS_RESULTSTR) {
564  /* Read result string from the IME. */
565  LONG len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); // Length is always in bytes, even in UNICODE build.
566  TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR));
567  len = ImmGetCompositionString(hIMC, GCS_RESULTSTR, str, len);
568  str[len / sizeof(TCHAR)] = '\0';
569 
570  /* Transmit text to windowing system. */
571  if (len > 0) {
572  HandleTextInput(NULL, true); // Clear marked string.
573  HandleTextInput(FS2OTTD(str));
574  }
575  SetCompositionPos(hwnd);
576 
577  /* Don't pass the result string on to the default window proc. */
578  lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR);
579  }
580 
581  if ((lParam & GCS_COMPSTR) && DrawIMECompositionString()) {
582  /* Read composition string from the IME. */
583  LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0); // Length is always in bytes, even in UNICODE build.
584  TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR));
585  len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str, len);
586  str[len / sizeof(TCHAR)] = '\0';
587 
588  if (len > 0) {
589  static char utf8_buf[1024];
590  convert_from_fs(str, utf8_buf, lengthof(utf8_buf));
591 
592  /* Convert caret position from bytes in the input string to a position in the UTF-8 encoded string. */
593  LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0);
594  const char *caret = utf8_buf;
595  for (const TCHAR *c = str; *c != '\0' && *caret != '\0' && caret_bytes > 0; c++, caret_bytes--) {
596  /* Skip DBCS lead bytes or leading surrogates. */
597 #ifdef UNICODE
598  if (Utf16IsLeadSurrogate(*c)) {
599 #else
600  if (IsDBCSLeadByte(*c)) {
601 #endif
602  c++;
603  caret_bytes--;
604  }
605  Utf8Consume(&caret);
606  }
607 
608  HandleTextInput(utf8_buf, true, caret);
609  } else {
610  HandleTextInput(NULL, true);
611  }
612 
613  lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART);
614  }
615  }
616  ImmReleaseContext(hwnd, hIMC);
617 
618  return lParam != 0 ? DefWindowProc(hwnd, WM_IME_COMPOSITION, wParam, lParam) : 0;
619 }
620 
622 static void CancelIMEComposition(HWND hwnd)
623 {
624  HIMC hIMC = ImmGetContext(hwnd);
625  if (hIMC != NULL) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
626  ImmReleaseContext(hwnd, hIMC);
627  /* Clear any marked string from the current edit box. */
628  HandleTextInput(NULL, true);
629 }
630 
631 static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
632 {
633  static uint32 keycode = 0;
634  static bool console = false;
635  static bool in_sizemove = false;
636 
637  switch (msg) {
638  case WM_CREATE:
639  SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, TrackMouseTimerProc);
640  SetCompositionPos(hwnd);
641  _imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
642  break;
643 
644  case WM_ENTERSIZEMOVE:
645  in_sizemove = true;
646  break;
647 
648  case WM_EXITSIZEMOVE:
649  in_sizemove = false;
650  break;
651 
652  case WM_PAINT:
653  if (!in_sizemove && _draw_mutex != NULL && !HasModalProgress()) {
654  /* Get the union of the old update rect and the new update rect. */
655  RECT r;
656  GetUpdateRect(hwnd, &r, FALSE);
657  UnionRect(&_wnd.update_rect, &_wnd.update_rect, &r);
658 
659  /* Mark the window as updated, otherwise Windows would send more WM_PAINT messages. */
660  ValidateRect(hwnd, NULL);
661  _draw_mutex->SendSignal();
662  } else {
663  PAINTSTRUCT ps;
664 
665  BeginPaint(hwnd, &ps);
666  PaintWindow(ps.hdc);
667  EndPaint(hwnd, &ps);
668  }
669  return 0;
670 
671  case WM_PALETTECHANGED:
672  if ((HWND)wParam == hwnd) return 0;
673  FALLTHROUGH;
674 
675  case WM_QUERYNEWPALETTE: {
676  HDC hDC = GetWindowDC(hwnd);
677  HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
678  UINT nChanged = RealizePalette(hDC);
679 
680  SelectPalette(hDC, hOldPalette, TRUE);
681  ReleaseDC(hwnd, hDC);
682  if (nChanged != 0) InvalidateRect(hwnd, NULL, FALSE);
683  return 0;
684  }
685 
686  case WM_CLOSE:
687  HandleExitGameRequest();
688  return 0;
689 
690  case WM_DESTROY:
691  if (_window_maximize) _cur_resolution = _bck_resolution;
692  return 0;
693 
694  case WM_LBUTTONDOWN:
695  SetCapture(hwnd);
696  _left_button_down = true;
698  return 0;
699 
700  case WM_LBUTTONUP:
701  ReleaseCapture();
702  _left_button_down = false;
703  _left_button_clicked = false;
705  return 0;
706 
707  case WM_RBUTTONDOWN:
708  SetCapture(hwnd);
709  _right_button_down = true;
710  _right_button_clicked = true;
712  return 0;
713 
714  case WM_RBUTTONUP:
715  ReleaseCapture();
716  _right_button_down = false;
718  return 0;
719 
720  case WM_MOUSELEAVE:
721  UndrawMouseCursor();
722  _cursor.in_window = false;
723 
724  if (!_left_button_down && !_right_button_down) MyShowCursor(true);
725  return 0;
726 
727  case WM_MOUSEMOVE: {
728  int x = (int16)LOWORD(lParam);
729  int y = (int16)HIWORD(lParam);
730 
731  /* If the mouse was not in the window and it has moved it means it has
732  * come into the window, so start drawing the mouse. Also start
733  * tracking the mouse for exiting the window */
734  if (!_cursor.in_window) {
735  _cursor.in_window = true;
736  if (_pTrackMouseEvent != NULL) {
737  TRACKMOUSEEVENT tme;
738  tme.cbSize = sizeof(tme);
739  tme.dwFlags = TME_LEAVE;
740  tme.hwndTrack = hwnd;
741 
742  _pTrackMouseEvent(&tme);
743  } else {
744  SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, TrackMouseTimerProc);
745  }
746  }
747 
748  if (_cursor.fix_at) {
749  /* Get all queued mouse events now in case we have to warp the cursor. In the
750  * end, we only care about the current mouse position and not bygone events. */
751  MSG m;
752  while (PeekMessage(&m, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE | PM_NOYIELD | PM_QS_INPUT)) {
753  x = (int16)LOWORD(m.lParam);
754  y = (int16)HIWORD(m.lParam);
755  }
756  }
757 
758  if (_cursor.UpdateCursorPosition(x, y, false)) {
759  POINT pt;
760  pt.x = _cursor.pos.x;
761  pt.y = _cursor.pos.y;
762  ClientToScreen(hwnd, &pt);
763  SetCursorPos(pt.x, pt.y);
764  }
765  MyShowCursor(false);
767  return 0;
768  }
769 
770  case WM_INPUTLANGCHANGE:
771  _imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
772  break;
773 
774  case WM_IME_SETCONTEXT:
775  /* Don't show the composition window if we draw the string ourself. */
776  if (DrawIMECompositionString()) lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
777  break;
778 
779  case WM_IME_STARTCOMPOSITION:
780  SetCompositionPos(hwnd);
781  if (DrawIMECompositionString()) return 0;
782  break;
783 
784  case WM_IME_COMPOSITION:
785  return HandleIMEComposition(hwnd, wParam, lParam);
786 
787  case WM_IME_ENDCOMPOSITION:
788  /* Clear any pending composition string. */
789  HandleTextInput(NULL, true);
790  if (DrawIMECompositionString()) return 0;
791  break;
792 
793  case WM_IME_NOTIFY:
794  if (wParam == IMN_OPENCANDIDATE) SetCandidatePos(hwnd);
795  break;
796 
797 #if !defined(UNICODE)
798  case WM_IME_CHAR:
799  if (GB(wParam, 8, 8) != 0) {
800  /* DBCS character, send lead byte first. */
801  HandleCharMsg(0, GB(wParam, 8, 8));
802  }
803  HandleCharMsg(0, GB(wParam, 0, 8));
804  return 0;
805 #endif
806 
807  case WM_DEADCHAR:
808  console = GB(lParam, 16, 8) == 41;
809  return 0;
810 
811  case WM_CHAR: {
812  uint scancode = GB(lParam, 16, 8);
813  uint charcode = wParam;
814 
815  /* If the console key is a dead-key, we need to press it twice to get a WM_CHAR message.
816  * But we then get two WM_CHAR messages, so ignore the first one */
817  if (console && scancode == 41) {
818  console = false;
819  return 0;
820  }
821 
822  /* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
823  * clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */
824  uint cur_keycode = keycode;
825  keycode = 0;
826 
827  return HandleCharMsg(cur_keycode, charcode);
828  }
829 
830  case WM_KEYDOWN: {
831  /* No matter the keyboard layout, we will map the '~' to the console. */
832  uint scancode = GB(lParam, 16, 8);
833  keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam);
834 
835  /* Silently drop all messages handled by WM_CHAR. */
836  MSG msg;
837  if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
838  if ((msg.message == WM_CHAR || msg.message == WM_DEADCHAR) && GB(lParam, 16, 8) == GB(msg.lParam, 16, 8)) {
839  return 0;
840  }
841  }
842 
843  uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
844 
845  /* No character translation? */
846  if (charcode == 0) {
847  HandleKeypress(keycode, 0);
848  return 0;
849  }
850 
851  /* Is the console key a dead key? If yes, ignore the first key down event. */
852  if (HasBit(charcode, 31) && !console) {
853  if (scancode == 41) {
854  console = true;
855  return 0;
856  }
857  }
858  console = false;
859 
860  /* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
861  * clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */
862  uint cur_keycode = keycode;
863  keycode = 0;
864 
865  return HandleCharMsg(cur_keycode, LOWORD(charcode));
866  }
867 
868  case WM_SYSKEYDOWN: // user presses F10 or Alt, both activating the title-menu
869  switch (wParam) {
870  case VK_RETURN:
871  case 'F': // Full Screen on ALT + ENTER/F
872  ToggleFullScreen(!_wnd.fullscreen);
873  return 0;
874 
875  case VK_MENU: // Just ALT
876  return 0; // do nothing
877 
878  case VK_F10: // F10, ignore activation of menu
879  HandleKeypress(MapWindowsKey(wParam), 0);
880  return 0;
881 
882  default: // ALT in combination with something else
883  HandleKeypress(MapWindowsKey(wParam), 0);
884  break;
885  }
886  break;
887 
888  case WM_SIZE:
889  if (wParam != SIZE_MINIMIZED) {
890  /* Set maximized flag when we maximize (obviously), but also when we
891  * switched to fullscreen from a maximized state */
892  _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
893  if (_window_maximize || _fullscreen) _bck_resolution = _cur_resolution;
894  ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
895  }
896  return 0;
897 
898  case WM_SIZING: {
899  RECT *r = (RECT*)lParam;
900  RECT r2;
901  int w, h;
902 
903  SetRect(&r2, 0, 0, 0, 0);
904  AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
905 
906  w = r->right - r->left - (r2.right - r2.left);
907  h = r->bottom - r->top - (r2.bottom - r2.top);
908  w = max(w, 64);
909  h = max(h, 64);
910  SetRect(&r2, 0, 0, w, h);
911 
912  AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
913  w = r2.right - r2.left;
914  h = r2.bottom - r2.top;
915 
916  switch (wParam) {
917  case WMSZ_BOTTOM:
918  r->bottom = r->top + h;
919  break;
920 
921  case WMSZ_BOTTOMLEFT:
922  r->bottom = r->top + h;
923  r->left = r->right - w;
924  break;
925 
926  case WMSZ_BOTTOMRIGHT:
927  r->bottom = r->top + h;
928  r->right = r->left + w;
929  break;
930 
931  case WMSZ_LEFT:
932  r->left = r->right - w;
933  break;
934 
935  case WMSZ_RIGHT:
936  r->right = r->left + w;
937  break;
938 
939  case WMSZ_TOP:
940  r->top = r->bottom - h;
941  break;
942 
943  case WMSZ_TOPLEFT:
944  r->top = r->bottom - h;
945  r->left = r->right - w;
946  break;
947 
948  case WMSZ_TOPRIGHT:
949  r->top = r->bottom - h;
950  r->right = r->left + w;
951  break;
952  }
953  return TRUE;
954  }
955 
956 /* needed for wheel */
957 #if !defined(WM_MOUSEWHEEL)
958 # define WM_MOUSEWHEEL 0x020A
959 #endif /* WM_MOUSEWHEEL */
960 #if !defined(GET_WHEEL_DELTA_WPARAM)
961 # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
962 #endif /* GET_WHEEL_DELTA_WPARAM */
963 
964  case WM_MOUSEWHEEL: {
965  int delta = GET_WHEEL_DELTA_WPARAM(wParam);
966 
967  if (delta < 0) {
968  _cursor.wheel++;
969  } else if (delta > 0) {
970  _cursor.wheel--;
971  }
973  return 0;
974  }
975 
976  case WM_SETFOCUS:
977  _wnd.has_focus = true;
978  SetCompositionPos(hwnd);
979  break;
980 
981  case WM_KILLFOCUS:
982  _wnd.has_focus = false;
983  break;
984 
985  case WM_ACTIVATE: {
986  /* Don't do anything if we are closing openttd */
987  if (_exit_game) break;
988 
989  bool active = (LOWORD(wParam) != WA_INACTIVE);
990  bool minimized = (HIWORD(wParam) != 0);
991  if (_wnd.fullscreen) {
992  if (active && minimized) {
993  /* Restore the game window */
994  ShowWindow(hwnd, SW_RESTORE);
995  static_cast<VideoDriver_Win32 *>(VideoDriver::GetInstance())->MakeWindow(true);
996  } else if (!active && !minimized) {
997  /* Minimise the window and restore desktop */
998  ShowWindow(hwnd, SW_MINIMIZE);
999  ChangeDisplaySettings(NULL, 0);
1000  }
1001  }
1002  break;
1003  }
1004  }
1005 
1006  return DefWindowProc(hwnd, msg, wParam, lParam);
1007 }
1008 
1009 static void RegisterWndClass()
1010 {
1011  static bool registered = false;
1012 
1013  if (!registered) {
1014  HINSTANCE hinst = GetModuleHandle(NULL);
1015  WNDCLASS wnd = {
1016  CS_OWNDC,
1017  WndProcGdi,
1018  0,
1019  0,
1020  hinst,
1021  LoadIcon(hinst, MAKEINTRESOURCE(100)),
1022  LoadCursor(NULL, IDC_ARROW),
1023  0,
1024  0,
1025  _T("OTTD")
1026  };
1027 
1028  registered = true;
1029  if (!RegisterClass(&wnd)) usererror("RegisterClass failed");
1030 
1031  /* Dynamically load mouse tracking, as it doesn't exist on Windows 95. */
1032  _pTrackMouseEvent = (PFNTRACKMOUSEEVENT)GetProcAddress(GetModuleHandle(_T("User32")), "TrackMouseEvent");
1033  }
1034 }
1035 
1036 static bool AllocateDibSection(int w, int h, bool force)
1037 {
1038  BITMAPINFO *bi;
1039  HDC dc;
1041 
1042  w = max(w, 64);
1043  h = max(h, 64);
1044 
1045  if (bpp == 0) usererror("Can't use a blitter that blits 0 bpp for normal visuals");
1046 
1047  if (!force && w == _screen.width && h == _screen.height) return false;
1048 
1049  bi = (BITMAPINFO*)alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);
1050  memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);
1051  bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1052 
1053  bi->bmiHeader.biWidth = _wnd.width = w;
1054  bi->bmiHeader.biHeight = -(_wnd.height = h);
1055 
1056  bi->bmiHeader.biPlanes = 1;
1057  bi->bmiHeader.biBitCount = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
1058  bi->bmiHeader.biCompression = BI_RGB;
1059 
1060  if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
1061 
1062  dc = GetDC(0);
1063  _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.buffer_bits, NULL, 0);
1064  if (_wnd.dib_sect == NULL) usererror("CreateDIBSection failed");
1065  ReleaseDC(0, dc);
1066 
1067  _screen.width = w;
1068  _screen.pitch = (bpp == 8) ? Align(w, 4) : w;
1069  _screen.height = h;
1070  _screen.dst_ptr = _wnd.buffer_bits;
1071 
1072  return true;
1073 }
1074 
1075 static const Dimension default_resolutions[] = {
1076  { 640, 480 },
1077  { 800, 600 },
1078  { 1024, 768 },
1079  { 1152, 864 },
1080  { 1280, 800 },
1081  { 1280, 960 },
1082  { 1280, 1024 },
1083  { 1400, 1050 },
1084  { 1600, 1200 },
1085  { 1680, 1050 },
1086  { 1920, 1200 }
1087 };
1088 
1089 static void FindResolutions()
1090 {
1091  uint n = 0;
1092  uint i;
1093  DEVMODEA dm;
1094 
1095  /* Check modes for the relevant fullscreen bpp */
1096  uint bpp = _support8bpp != S8BPP_HARDWARE ? 32 : BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
1097 
1098  /* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95
1099  * Doesn't really matter since we don't pass a string anyways, but still
1100  * a letdown */
1101  for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) {
1102  if (dm.dmBitsPerPel == bpp &&
1103  dm.dmPelsWidth >= 640 && dm.dmPelsHeight >= 480) {
1104  uint j;
1105 
1106  for (j = 0; j < n; j++) {
1107  if (_resolutions[j].width == dm.dmPelsWidth && _resolutions[j].height == dm.dmPelsHeight) break;
1108  }
1109 
1110  /* In the previous loop we have checked already existing/added resolutions if
1111  * they are the same as the new ones. If this is not the case (j == n); we have
1112  * looped all and found none, add the new one to the list. If we have reached the
1113  * maximum amount of resolutions, then quit querying the display */
1114  if (j == n) {
1115  _resolutions[j].width = dm.dmPelsWidth;
1116  _resolutions[j].height = dm.dmPelsHeight;
1117  if (++n == lengthof(_resolutions)) break;
1118  }
1119  }
1120  }
1121 
1122  /* We have found no resolutions, show the default list */
1123  if (n == 0) {
1124  memcpy(_resolutions, default_resolutions, sizeof(default_resolutions));
1125  n = lengthof(default_resolutions);
1126  }
1127 
1128  _num_resolutions = n;
1129  SortResolutions(_num_resolutions);
1130 }
1131 
1132 static FVideoDriver_Win32 iFVideoDriver_Win32;
1133 
1134 const char *VideoDriver_Win32::Start(const char * const *parm)
1135 {
1136  memset(&_wnd, 0, sizeof(_wnd));
1137 
1138  RegisterWndClass();
1139 
1140  MakePalette();
1141 
1142  FindResolutions();
1143 
1144  DEBUG(driver, 2, "Resolution for display: %ux%u", _cur_resolution.width, _cur_resolution.height);
1145 
1146  /* fullscreen uses those */
1147  _wnd.width_org = _cur_resolution.width;
1148  _wnd.height_org = _cur_resolution.height;
1149 
1150  AllocateDibSection(_cur_resolution.width, _cur_resolution.height);
1151  this->MakeWindow(_fullscreen);
1152 
1154 
1155  _draw_threaded = GetDriverParam(parm, "no_threads") == NULL && GetDriverParam(parm, "no_thread") == NULL && GetCPUCoreCount() > 1;
1156 
1157  return NULL;
1158 }
1159 
1161 {
1162  DeleteObject(_wnd.gdi_palette);
1163  DeleteObject(_wnd.dib_sect);
1164  DestroyWindow(_wnd.main_wnd);
1165 
1166  if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0);
1167  MyShowCursor(true);
1168 }
1169 
1170 void VideoDriver_Win32::MakeDirty(int left, int top, int width, int height)
1171 {
1172  RECT r = { left, top, left + width, top + height };
1173 
1174  InvalidateRect(_wnd.main_wnd, &r, FALSE);
1175 }
1176 
1177 static void CheckPaletteAnim()
1178 {
1179  if (_cur_palette.count_dirty == 0) return;
1180 
1181  _local_palette = _cur_palette;
1182  InvalidateRect(_wnd.main_wnd, NULL, FALSE);
1183 }
1184 
1186 {
1187  MSG mesg;
1188  uint32 cur_ticks = GetTickCount();
1189  uint32 last_cur_ticks = cur_ticks;
1190  uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
1191 
1192  if (_draw_threaded) {
1193  /* Initialise the mutex first, because that's the thing we *need*
1194  * directly in the newly created thread. */
1195  _draw_mutex = ThreadMutex::New();
1196  _draw_thread_initialized = CreateEvent(NULL, FALSE, FALSE, NULL);
1197  if (_draw_mutex == NULL || _draw_thread_initialized == NULL) {
1198  _draw_threaded = false;
1199  } else {
1200  _draw_continue = true;
1201  _draw_threaded = ThreadObject::New(&PaintWindowThread, NULL, &_draw_thread, "ottd:draw-win32");
1202 
1203  /* Free the mutex if we won't be able to use it. */
1204  if (!_draw_threaded) {
1205  delete _draw_mutex;
1206  _draw_mutex = NULL;
1207  CloseHandle(_draw_thread_initialized);
1208  _draw_thread_initialized = NULL;
1209  } else {
1210  DEBUG(driver, 1, "Threaded drawing enabled");
1211  /* Wait till the draw thread has started itself. */
1212  WaitForSingleObject(_draw_thread_initialized, INFINITE);
1213  _draw_mutex->BeginCritical();
1214  }
1215  }
1216  }
1217 
1218  _wnd.running = true;
1219 
1220  CheckPaletteAnim();
1221  for (;;) {
1222  uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
1223 
1224  while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
1225  InteractiveRandom(); // randomness
1226  /* Convert key messages to char messages if we want text input. */
1227  if (EditBoxInGlobalFocus()) TranslateMessage(&mesg);
1228  DispatchMessage(&mesg);
1229  }
1230  if (_exit_game) return;
1231 
1232 #if defined(_DEBUG)
1233  if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 &&
1234 #else
1235  /* Speed up using TAB, but disable for ALT+TAB of course */
1236  if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 &&
1237 #endif
1238  !_networking && _game_mode != GM_MENU) {
1239  _fast_forward |= 2;
1240  } else if (_fast_forward & 2) {
1241  _fast_forward = 0;
1242  }
1243 
1244  cur_ticks = GetTickCount();
1245  if (cur_ticks >= next_tick || (_fast_forward && !_pause_mode) || cur_ticks < prev_cur_ticks) {
1246  _realtime_tick += cur_ticks - last_cur_ticks;
1247  last_cur_ticks = cur_ticks;
1248  next_tick = cur_ticks + MILLISECONDS_PER_TICK;
1249 
1250  bool old_ctrl_pressed = _ctrl_pressed;
1251 
1252  _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0;
1253  _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0;
1254 
1255  /* determine which directional keys are down */
1256  if (_wnd.has_focus) {
1257  _dirkeys =
1258  (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
1259  (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
1260  (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
1261  (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
1262  } else {
1263  _dirkeys = 0;
1264  }
1265 
1266  if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
1267 
1268  /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
1269  GdiFlush();
1270 
1271  /* The game loop is the part that can run asynchronously.
1272  * The rest except sleeping can't. */
1273  if (_draw_threaded) _draw_mutex->EndCritical();
1274  GameLoop();
1275  if (_draw_threaded) _draw_mutex->BeginCritical();
1276 
1277  if (_force_full_redraw) MarkWholeScreenDirty();
1278 
1279  UpdateWindows();
1280  CheckPaletteAnim();
1281  } else {
1282  /* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
1283  GdiFlush();
1284 
1285  /* Release the thread while sleeping */
1286  if (_draw_threaded) _draw_mutex->EndCritical();
1287  Sleep(1);
1288  if (_draw_threaded) _draw_mutex->BeginCritical();
1289 
1291  DrawMouseCursor();
1292  }
1293  }
1294 
1295  if (_draw_threaded) {
1296  _draw_continue = false;
1297  /* Sending signal if there is no thread blocked
1298  * is very valid and results in noop */
1299  _draw_mutex->SendSignal();
1300  _draw_mutex->EndCritical();
1301  _draw_thread->Join();
1302 
1303  CloseHandle(_draw_thread_initialized);
1304  delete _draw_mutex;
1305  delete _draw_thread;
1306  }
1307 }
1308 
1310 {
1311  if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
1312  if (_window_maximize) ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL);
1313 
1314  _wnd.width = _wnd.width_org = w;
1315  _wnd.height = _wnd.height_org = h;
1316 
1317  bool ret = this->MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching
1318  if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
1319  return ret;
1320 }
1321 
1323 {
1324  if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
1325  bool ret = this->MakeWindow(full_screen);
1326  if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
1327  return ret;
1328 }
1329 
1331 {
1332  return AllocateDibSection(_screen.width, _screen.height, true) && this->MakeWindow(_fullscreen);
1333 }
1334 
1336 {
1337  if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
1338 }
1339 
1341 {
1342  if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
1343 }
1344 
1346 {
1347  if (_draw_mutex != NULL) _draw_mutex->BeginCritical(true);
1348  CancelIMEComposition(_wnd.main_wnd);
1349  SetCompositionPos(_wnd.main_wnd);
1350  SetCandidatePos(_wnd.main_wnd);
1351  if (_draw_mutex != NULL) _draw_mutex->EndCritical(true);
1352 }
static LRESULT HandleCharMsg(uint keycode, WChar charcode)
Forward key presses to the window system.
Definition: win32_v.cpp:433
const char * GetDriverParam(const char *const *parm, const char *name)
Get a string parameter the list of parameters.
Definition: driver.cpp:40
void HandleTextInput(const char *str, bool marked=false, const char *caret=NULL, const char *insert_location=NULL, const char *replacement_end=NULL)
Handle text input.
Definition: window.cpp:2742
static bool _draw_threaded
Whether the drawing is/may be done in a separate thread.
Definition: win32_v.cpp:67
bool _networking
are we in networking mode?
Definition: network.cpp:56
void MainLoop()
Perform the actual drawing.
Definition: win32_v.cpp:1185
uint32 _realtime_tick
The real time in the game.
Definition: debug.cpp:52
const NWidgetCore * nested_focus
Currently focused nested widget, or NULL if no nested widget has focus.
Definition: window_gui.h:329
Point pos
logical mouse position
Definition: gfx_type.h:119
const char * FS2OTTD(const TCHAR *name)
Convert to OpenTTD&#39;s encoding from that of the local environment.
Definition: win32.cpp:565
Information about the currently used palette.
Definition: gfx_type.h:309
static HANDLE _draw_thread_initialized
Event that is signaled when the drawing thread has finished initializing.
Definition: win32_v.cpp:73
int left
x position of left edge of the window
Definition: window_gui.h:319
, Comma
Definition: gfx_type.h:104
The factory for Windows&#39; video driver.
Definition: win32_v.h:48
bool ChangeResolution(int w, int h)
Change the resolution of the window.
Definition: win32_v.cpp:1309
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:409
bool _right_button_down
Is right mouse button pressed?
Definition: gfx.cpp:41
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Definition: gfx_type.h:310
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:22
Cross-platform Mutex.
Definition: thread.h:56
= Equals
Definition: gfx_type.h:99
int top
y position of top edge of the window
Definition: window_gui.h:320
Dimension _cur_resolution
The current resolution.
Definition: driver.cpp:24
int _num_resolutions
The number of resolutions.
Definition: driver.cpp:22
static volatile bool _draw_continue
Should we keep continue drawing?
Definition: win32_v.cpp:75
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
No palette animation.
Definition: base.hpp:52
How all blitters should look like.
Definition: base.hpp:30
char * convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
Convert to OpenTTD&#39;s encoding from that of the environment in UNICODE.
Definition: win32.cpp:598
RAII class for measuring simple elements of performance.
static T max(const T a, const T b)
Returns the maximum of two values.
Definition: math_func.hpp:26
virtual void EndCritical(bool allow_recursive=false)=0
End of the critical section.
virtual void PostResize()
Post resize event.
Definition: base.hpp:203
Palette animation should be done by video backend (8bpp only!)
Definition: base.hpp:53
virtual void SendSignal()=0
Send a signal and wake the &#39;thread&#39; that was waiting for it.
bool _left_button_clicked
Is left mouse button clicked?
Definition: gfx.cpp:40
Console; Window numbers:
Definition: window_type.h:633
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:36
bool ToggleFullscreen(bool fullscreen)
Change the full screen setting.
Definition: win32_v.cpp:1322
&#39; Single quote
Definition: gfx_type.h:103
bool _right_button_clicked
Is right mouse button clicked?
Definition: gfx.cpp:42
uint pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:178
static T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition: math_func.hpp:97
The blitter takes care of the palette animation.
Definition: base.hpp:54
uint current_y
Current vertical size (after resizing).
Definition: widget_type.h:175
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
bool _left_button_down
Is left mouse button pressed?
Definition: gfx.cpp:39
[ Left square bracket
Definition: gfx_type.h:100
] Right square bracket
Definition: gfx_type.h:102
\ Backslash
Definition: gfx_type.h:101
void GetKeyboardLayout()
Retrieve keyboard layout from language string or (if set) config file.
Definition: osk_gui.cpp:357
static bool DrawIMECompositionString()
Should we draw the composition string ourself, i.e is this a normal IME?
Definition: win32_v.cpp:496
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:92
int wheel
mouse wheel movement
Definition: gfx_type.h:121
bool UpdateCursorPosition(int x, int y, bool queued_warp)
Update cursor position on mouse movement.
Definition: gfx.cpp:1648
/ Forward slash
Definition: gfx_type.h:97
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
Definition: gfx_type.h:306
static ThreadMutex * New()
Create a new mutex.
Definition: thread_none.cpp:32
void HandleKeypress(uint keycode, WChar key)
Handle keyboard input.
Definition: window.cpp:2654
byte _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
Definition: gfx.cpp:32
static ThreadObject * _draw_thread
Thread used to &#39;draw&#39; to the screen, i.e.
Definition: win32_v.cpp:69
bool fix_at
mouse is moving, but cursor is not (used for scrolling)
Definition: gfx_type.h:122
void HandleMouseEvents()
Handle a mouse event from the video driver.
Definition: window.cpp:2961
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
PauseModeByte _pause_mode
The current pause mode.
Definition: gfx.cpp:48
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition: factory.hpp:147
uint pos_x
Horizontal position of top-left corner of the widget in the window.
Definition: widget_type.h:177
int first_dirty
The first dirty element.
Definition: gfx_type.h:311
bool AfterBlitterChange()
Callback invoked after the blitter was changed.
Definition: win32_v.cpp:1330
static WChar Utf16DecodeSurrogate(uint lead, uint trail)
Convert an UTF-16 surrogate pair to the corresponding Unicode character.
Definition: string_func.h:185
; Semicolon
Definition: gfx_type.h:98
Palette _cur_palette
Current palette.
Definition: gfx.cpp:49
bool _shift_pressed
Is Shift pressed?
Definition: gfx.cpp:37
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:36
Dimension _resolutions[32]
List of resolutions.
Definition: driver.cpp:23
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
static void PaintWindow(HDC dc)
Do palette animation and blit to the window.
Definition: win32_v.cpp:361
void Stop()
Stop this driver.
Definition: win32_v.cpp:1160
The video driver for windows.
Definition: win32_v.h:18
const char * Start(const char *const *param)
Start this driver.
Definition: win32_v.cpp:1134
static bool Utf16IsTrailSurrogate(uint c)
Is the given character a lead surrogate code point?
Definition: string_func.h:174
void AcquireBlitterLock()
Acquire any lock(s) required to be held when changing blitters.
Definition: win32_v.cpp:1335
void HandleCtrlChanged()
State of CONTROL key has changed.
Definition: window.cpp:2711
virtual void Join()=0
Join this thread.
void EditBoxLostFocus()
An edit box lost the input focus.
Definition: win32_v.cpp:1345
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Definition: window.cpp:459
Base of the Windows video driver.
Speed of painting drawn video buffer.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam)
Handle WM_IME_COMPOSITION messages.
Definition: win32_v.cpp:558
void MakeDirty(int left, int top, int width, int height)
Mark a particular area dirty.
Definition: win32_v.cpp:1170
static void SetCandidatePos(HWND hwnd)
Set the position of the candidate window.
Definition: win32_v.cpp:524
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
void NetworkDrawChatMessage()
Draw the chat message-box.
uint current_x
Current horizontal size (after resizing).
Definition: widget_type.h:174
static Palette _local_palette
Local copy of the palette for use in the drawing thread.
Definition: win32_v.cpp:77
#define endof(x)
Get the end element of an fixed size array.
Definition: stdafx.h:412
bool in_window
mouse inside this window, determines drawing logic
Definition: gfx_type.h:143
void ReleaseBlitterLock()
Release any lock(s) required to be held when changing blitters.
Definition: win32_v.cpp:1340
bool MakeWindow(bool full_screen)
Instantiate a new window.
Definition: win32_v.cpp:264
Coordinates of a point in 2D.
virtual void WaitForSignal()=0
Wait for a signal to be send.
virtual uint8 GetScreenDepth()=0
Get the screen depth this blitter works for.
#define AS(ap_name, size_x, size_y, min_year, max_year, catchment, noise, maint_cost, ttdpatch_type, class_id, name, preview)
AirportSpec definition for airports with at least one depot.
uint GetCPUCoreCount()
Get number of processor cores in the system, including HyperThreading or similar. ...
Definition: os2.cpp:219
static bool Utf16IsLeadSurrogate(uint c)
Is the given character a lead surrogate code point?
Definition: string_func.h:164
void GameSizeChanged()
Size of the application screen changed.
Definition: main_gui.cpp:606
int width
width of the window (number of pixels to the right in x direction)
Definition: window_gui.h:321
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
. Period
Definition: gfx_type.h:105
WindowClass window_class
Window class.
Definition: window_gui.h:313
virtual void BeginCritical(bool allow_recursive=false)=0
Begin the critical section.
virtual bool Exit()=0
Exit this thread.
int count_dirty
The number of dirty elements.
Definition: gfx_type.h:312
static void CancelIMEComposition(HWND hwnd)
Clear the current composition string.
Definition: win32_v.cpp:622
virtual Point GetCaretPosition() const
Get the current caret position if an edit box has the focus.
Definition: window.cpp:392
uint32 WChar
Type for wide characters, i.e.
Definition: string_type.h:35
static ThreadMutex * _draw_mutex
Mutex to keep the access to the shared memory controlled.
Definition: win32_v.cpp:71
A Thread Object which works on all our supported OSes.
Definition: thread.h:24
static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread=NULL, const char *name=NULL)
Create a thread; proc will be called as first function inside the thread, with optional params...
static void SetCompositionPos(HWND hwnd)
Set position of the composition window to the caret position.
Definition: win32_v.cpp:502
Dimensions (a width and height) of a rectangle in 2D.
Full 8bpp support by OS and hardware.
Definition: gfx_type.h:319
static bool HasModalProgress()
Check if we are currently in a modal progress state.
Definition: progress.h:23
int height
Height of the window (number of pixels down in y direction)
Definition: window_gui.h:322
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition: gfx.cpp:1463
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.
Definition: window.cpp:3112