src/video/win32_v.cpp
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5648 1608018c5ff2
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
       
     1 /* $Id$ */
       
     2 
       
     3 #include "../stdafx.h"
       
     4 #include "../openttd.h"
       
     5 #include "../functions.h"
       
     6 #include "../gfx.h"
       
     7 #include "../macros.h"
       
     8 #include "../network/network.h"
       
     9 #include "../variables.h"
       
    10 #include "../win32.h"
       
    11 #include "../window.h"
       
    12 #include "win32_v.h"
       
    13 #include <windows.h>
       
    14 #include <tchar.h>
       
    15 
       
    16 static struct {
       
    17 	HWND main_wnd;
       
    18 	HBITMAP dib_sect;
       
    19 	Pixel *bitmap_bits;
       
    20 	Pixel *buffer_bits;
       
    21 	Pixel *alloced_bits;
       
    22 	HPALETTE gdi_palette;
       
    23 	int width;
       
    24 	int height;
       
    25 	int width_org;
       
    26 	int height_org;
       
    27 	bool fullscreen;
       
    28 	bool double_size;
       
    29 	bool has_focus;
       
    30 	bool running;
       
    31 } _wnd;
       
    32 
       
    33 bool _force_full_redraw;
       
    34 bool _double_size;
       
    35 bool _window_maximize;
       
    36 uint _display_hz;
       
    37 uint _fullscreen_bpp;
       
    38 static uint16 _bck_resolution[2];
       
    39 
       
    40 static void MakePalette(void)
       
    41 {
       
    42 	LOGPALETTE *pal;
       
    43 	uint i;
       
    44 
       
    45 	pal = alloca(sizeof(LOGPALETTE) + (256-1) * sizeof(PALETTEENTRY));
       
    46 
       
    47 	pal->palVersion = 0x300;
       
    48 	pal->palNumEntries = 256;
       
    49 
       
    50 	for (i = 0; i != 256; i++) {
       
    51 		pal->palPalEntry[i].peRed   = _cur_palette[i].r;
       
    52 		pal->palPalEntry[i].peGreen = _cur_palette[i].g;
       
    53 		pal->palPalEntry[i].peBlue  = _cur_palette[i].b;
       
    54 		pal->palPalEntry[i].peFlags = 0;
       
    55 
       
    56 	}
       
    57 	_wnd.gdi_palette = CreatePalette(pal);
       
    58 	if (_wnd.gdi_palette == NULL) error("CreatePalette failed!\n");
       
    59 }
       
    60 
       
    61 static void UpdatePalette(HDC dc, uint start, uint count)
       
    62 {
       
    63 	RGBQUAD rgb[256];
       
    64 	uint i;
       
    65 
       
    66 	for (i = 0; i != count; i++) {
       
    67 		rgb[i].rgbRed   = _cur_palette[start + i].r;
       
    68 		rgb[i].rgbGreen = _cur_palette[start + i].g;
       
    69 		rgb[i].rgbBlue  = _cur_palette[start + i].b;
       
    70 		rgb[i].rgbReserved = 0;
       
    71 	}
       
    72 
       
    73 	SetDIBColorTable(dc, start, count, rgb);
       
    74 }
       
    75 
       
    76 typedef struct {
       
    77 	byte vk_from;
       
    78 	byte vk_count;
       
    79 	byte map_to;
       
    80 } VkMapping;
       
    81 
       
    82 #define AS(x, z) {x, 0, z}
       
    83 #define AM(x, y, z, w) {x, y - x, z}
       
    84 
       
    85 static const VkMapping _vk_mapping[] = {
       
    86 	// Pageup stuff + up/down
       
    87 	AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN),
       
    88 	// Map letters & digits
       
    89 	AM('A','Z','A','Z'),
       
    90 	AM('0','9','0','9'),
       
    91 
       
    92 	AS(VK_ESCAPE,   WKC_ESC),
       
    93 	AS(VK_PAUSE,    WKC_PAUSE),
       
    94 	AS(VK_BACK,     WKC_BACKSPACE),
       
    95 	AM(VK_INSERT,   VK_DELETE, WKC_INSERT, WKC_DELETE),
       
    96 
       
    97 	AS(VK_SPACE,    WKC_SPACE),
       
    98 	AS(VK_RETURN,   WKC_RETURN),
       
    99 	AS(VK_TAB,      WKC_TAB),
       
   100 
       
   101 	// Function keys
       
   102 	AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
       
   103 
       
   104 	// Numeric part.
       
   105 	// What is the virtual keycode for numeric enter??
       
   106 	AM(VK_NUMPAD0, VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9),
       
   107 	AS(VK_DIVIDE,   WKC_NUM_DIV),
       
   108 	AS(VK_MULTIPLY, WKC_NUM_MUL),
       
   109 	AS(VK_SUBTRACT, WKC_NUM_MINUS),
       
   110 	AS(VK_ADD,      WKC_NUM_PLUS),
       
   111 	AS(VK_DECIMAL,  WKC_NUM_DECIMAL)
       
   112 };
       
   113 
       
   114 static uint MapWindowsKey(uint sym)
       
   115 {
       
   116 	const VkMapping *map;
       
   117 	uint key = 0;
       
   118 
       
   119 	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
       
   120 		if ((uint)(sym - map->vk_from) <= map->vk_count) {
       
   121 			key = sym - map->vk_from + map->map_to;
       
   122 			break;
       
   123 		}
       
   124 	}
       
   125 
       
   126 	if (GetAsyncKeyState(VK_SHIFT)   < 0) key |= WKC_SHIFT;
       
   127 	if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
       
   128 	if (GetAsyncKeyState(VK_MENU)    < 0) key |= WKC_ALT;
       
   129 	return key;
       
   130 }
       
   131 
       
   132 static bool AllocateDibSection(int w, int h);
       
   133 
       
   134 static void ClientSizeChanged(int w, int h)
       
   135 {
       
   136 	if (_wnd.double_size) {
       
   137 		w /= 2;
       
   138 		h /= 2;
       
   139 	}
       
   140 
       
   141 	// allocate new dib section of the new size
       
   142 	if (AllocateDibSection(w, h)) {
       
   143 		// mark all palette colors dirty
       
   144 		_pal_first_dirty = 0;
       
   145 		_pal_last_dirty = 255;
       
   146 		GameSizeChanged();
       
   147 
       
   148 		// redraw screen
       
   149 		if (_wnd.running) {
       
   150 			_screen.dst_ptr = _wnd.buffer_bits;
       
   151 			UpdateWindows();
       
   152 		}
       
   153 	}
       
   154 }
       
   155 
       
   156 #ifdef _DEBUG
       
   157 // Keep this function here..
       
   158 // It allows you to redraw the screen from within the MSVC debugger
       
   159 int RedrawScreenDebug(void)
       
   160 {
       
   161 	HDC dc,dc2;
       
   162 	static int _fooctr;
       
   163 	HBITMAP old_bmp;
       
   164 	HPALETTE old_palette;
       
   165 
       
   166 	_screen.dst_ptr = _wnd.buffer_bits;
       
   167 	UpdateWindows();
       
   168 
       
   169 	dc = GetDC(_wnd.main_wnd);
       
   170 	dc2 = CreateCompatibleDC(dc);
       
   171 
       
   172 	old_bmp = SelectObject(dc2, _wnd.dib_sect);
       
   173 	old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
       
   174 	BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
       
   175 	SelectPalette(dc, old_palette, TRUE);
       
   176 	SelectObject(dc2, old_bmp);
       
   177 	DeleteDC(dc2);
       
   178 	ReleaseDC(_wnd.main_wnd, dc);
       
   179 
       
   180 	return _fooctr++;
       
   181 }
       
   182 #endif
       
   183 
       
   184 /* Windows 95 will not have a WM_MOUSELEAVE message, so define it if needed */
       
   185 #if !defined(WM_MOUSELEAVE)
       
   186 #define WM_MOUSELEAVE 0x02A3
       
   187 #endif
       
   188 #define TID_POLLMOUSE 1
       
   189 #define MOUSE_POLL_DELAY 75
       
   190 
       
   191 static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time)
       
   192 {
       
   193 	RECT rc;
       
   194 	POINT pt;
       
   195 
       
   196 	/* Get the rectangle of our window and translate it to screen coordinates.
       
   197 	 * Compare this with the current screen coordinates of the mouse and if it
       
   198 	 * falls outside of the area or our window we have left the window. */
       
   199 	GetClientRect(hwnd, &rc);
       
   200 	MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2);
       
   201 	GetCursorPos(&pt);
       
   202 
       
   203 	if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) {
       
   204 		KillTimer(hwnd, event);
       
   205 		PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L);
       
   206 	}
       
   207 }
       
   208 
       
   209 static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       
   210 {
       
   211 	switch (msg) {
       
   212 		case WM_CREATE:
       
   213 			SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
       
   214 			break;
       
   215 
       
   216 		case WM_PAINT: {
       
   217 			PAINTSTRUCT ps;
       
   218 			HDC dc,dc2;
       
   219 			HBITMAP old_bmp;
       
   220 			HPALETTE old_palette;
       
   221 
       
   222 			BeginPaint(hwnd, &ps);
       
   223 			dc = ps.hdc;
       
   224 			dc2 = CreateCompatibleDC(dc);
       
   225 			old_bmp = SelectObject(dc2, _wnd.dib_sect);
       
   226 			old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
       
   227 
       
   228 			if (_pal_last_dirty != -1) {
       
   229 				UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1);
       
   230 				_pal_last_dirty = -1;
       
   231 			}
       
   232 
       
   233 			BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
       
   234 			SelectPalette(dc, old_palette, TRUE);
       
   235 			SelectObject(dc2, old_bmp);
       
   236 			DeleteDC(dc2);
       
   237 			EndPaint(hwnd, &ps);
       
   238 			return 0;
       
   239 		}
       
   240 
       
   241 		case WM_PALETTECHANGED:
       
   242 			if ((HWND)wParam == hwnd) return 0;
       
   243 			/* FALLTHROUGH */
       
   244 
       
   245 		case WM_QUERYNEWPALETTE: {
       
   246 			HDC hDC = GetWindowDC(hwnd);
       
   247 			HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
       
   248 			UINT nChanged = RealizePalette(hDC);
       
   249 
       
   250 			SelectPalette(hDC, hOldPalette, TRUE);
       
   251 			ReleaseDC(hwnd, hDC);
       
   252 			if (nChanged) InvalidateRect(hwnd, NULL, FALSE);
       
   253 			return 0;
       
   254 		}
       
   255 
       
   256 		case WM_CLOSE:
       
   257 			HandleExitGameRequest();
       
   258 			return 0;
       
   259 
       
   260 		case WM_DESTROY:
       
   261 			if (_window_maximize) {
       
   262 				_cur_resolution[0] = _bck_resolution[0];
       
   263 				_cur_resolution[1] = _bck_resolution[1];
       
   264 			}
       
   265 			return 0;
       
   266 
       
   267 		case WM_LBUTTONDOWN:
       
   268 			SetCapture(hwnd);
       
   269 			_left_button_down = true;
       
   270 			HandleMouseEvents();
       
   271 			return 0;
       
   272 
       
   273 		case WM_LBUTTONUP:
       
   274 			ReleaseCapture();
       
   275 			_left_button_down = false;
       
   276 			_left_button_clicked = false;
       
   277 			HandleMouseEvents();
       
   278 			return 0;
       
   279 
       
   280 		case WM_RBUTTONDOWN:
       
   281 			SetCapture(hwnd);
       
   282 			_right_button_down = true;
       
   283 			_right_button_clicked = true;
       
   284 			HandleMouseEvents();
       
   285 			return 0;
       
   286 
       
   287 		case WM_RBUTTONUP:
       
   288 			ReleaseCapture();
       
   289 			_right_button_down = false;
       
   290 			HandleMouseEvents();
       
   291 			return 0;
       
   292 
       
   293 		case WM_MOUSELEAVE:
       
   294 			UndrawMouseCursor();
       
   295 			_cursor.in_window = false;
       
   296 
       
   297 			if (!_left_button_down && !_right_button_down) MyShowCursor(true);
       
   298 			HandleMouseEvents();
       
   299 			return 0;
       
   300 
       
   301 		case WM_MOUSEMOVE: {
       
   302 			int x = (int16)LOWORD(lParam);
       
   303 			int y = (int16)HIWORD(lParam);
       
   304 			POINT pt;
       
   305 
       
   306 			/* If the mouse was not in the window and it has moved it means it has
       
   307 			 * come into the window, so start drawing the mouse. Also start
       
   308 			 * tracking the mouse for exiting the window */
       
   309 			if (!_cursor.in_window) {
       
   310 				_cursor.in_window = true;
       
   311 				SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
       
   312 
       
   313 				DrawMouseCursor();
       
   314 			}
       
   315 
       
   316 			if (_wnd.double_size) {
       
   317 				x /= 2;
       
   318 				y /= 2;
       
   319 			}
       
   320 
       
   321 			if (_cursor.fix_at) {
       
   322 				int dx = x - _cursor.pos.x;
       
   323 				int dy = y - _cursor.pos.y;
       
   324 				if (dx != 0 || dy != 0) {
       
   325 					_cursor.delta.x += dx;
       
   326 					_cursor.delta.y += dy;
       
   327 
       
   328 					pt.x = _cursor.pos.x;
       
   329 					pt.y = _cursor.pos.y;
       
   330 
       
   331 					if (_wnd.double_size) {
       
   332 						pt.x *= 2;
       
   333 						pt.y *= 2;
       
   334 					}
       
   335 					ClientToScreen(hwnd, &pt);
       
   336 					SetCursorPos(pt.x, pt.y);
       
   337 				}
       
   338 			} else {
       
   339 				_cursor.delta.x += x - _cursor.pos.x;
       
   340 				_cursor.delta.y += y - _cursor.pos.y;
       
   341 				_cursor.pos.x = x;
       
   342 				_cursor.pos.y = y;
       
   343 				_cursor.dirty = true;
       
   344 			}
       
   345 			MyShowCursor(false);
       
   346 			HandleMouseEvents();
       
   347 			return 0;
       
   348 		}
       
   349 
       
   350 		case WM_KEYDOWN: {
       
   351 			// this is the rewritten ascii input function
       
   352 			// it disables windows deadkey handling --> more linux like :D
       
   353 			wchar_t w = 0;
       
   354 			byte ks[256];
       
   355 			uint scancode;
       
   356 			uint32 pressed_key;
       
   357 
       
   358 			GetKeyboardState(ks);
       
   359 			if (ToUnicode(wParam, 0, ks, &w, 1, 0) != 1) {
       
   360 				/* On win9x ToUnicode always fails, so fall back to ToAscii */
       
   361 				if (ToAscii(wParam, 0, ks, &w, 0) != 1) w = 0; // no translation was possible
       
   362 			}
       
   363 
       
   364 			pressed_key = w | MapWindowsKey(wParam) << 16;
       
   365 
       
   366 			scancode = GB(lParam, 16, 8);
       
   367 			if (scancode == 41) pressed_key = w | WKC_BACKQUOTE << 16;
       
   368 
       
   369 			if (GB(pressed_key, 16, 16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) {
       
   370 				_double_size ^= 1;
       
   371 				_wnd.double_size = _double_size;
       
   372 				ClientSizeChanged(_wnd.width, _wnd.height);
       
   373 				MarkWholeScreenDirty();
       
   374 			}
       
   375 			HandleKeypress(pressed_key);
       
   376 			break;
       
   377 		}
       
   378 
       
   379 		case WM_SYSKEYDOWN: /* user presses F10 or Alt, both activating the title-menu */
       
   380 			switch (wParam) {
       
   381 				case VK_RETURN:
       
   382 				case 'F': /* Full Screen on ALT + ENTER/F */
       
   383 					ToggleFullScreen(!_wnd.fullscreen);
       
   384 					return 0;
       
   385 
       
   386 				case VK_MENU: /* Just ALT */
       
   387 					return 0; // do nothing
       
   388 
       
   389 				case VK_F10: /* F10, ignore activation of menu */
       
   390 					HandleKeypress(MapWindowsKey(wParam) << 16);
       
   391 					return 0;
       
   392 
       
   393 				default: /* ALT in combination with something else */
       
   394 					HandleKeypress(MapWindowsKey(wParam) << 16);
       
   395 					break;
       
   396 			}
       
   397 			break;
       
   398 
       
   399 		case WM_SIZE:
       
   400 			if (wParam != SIZE_MINIMIZED) {
       
   401 				/* Set maximized flag when we maximize (obviously), but also when we
       
   402 				 * switched to fullscreen from a maximized state */
       
   403 				_window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
       
   404 				if (_window_maximize) {
       
   405 					_bck_resolution[0] = _cur_resolution[0];
       
   406 					_bck_resolution[1] = _cur_resolution[1];
       
   407 				}
       
   408 				ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
       
   409 			}
       
   410 			return 0;
       
   411 
       
   412 		case WM_SIZING: {
       
   413 			RECT* r = (RECT*)lParam;
       
   414 			RECT r2;
       
   415 			int w, h;
       
   416 
       
   417 			SetRect(&r2, 0, 0, 0, 0);
       
   418 			AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
       
   419 
       
   420 			w = r->right - r->left - (r2.right - r2.left);
       
   421 			h = r->bottom - r->top - (r2.bottom - r2.top);
       
   422 			if (_wnd.double_size) {
       
   423 				w /= 2;
       
   424 				h /= 2;
       
   425 			}
       
   426 			w = clamp(w, 64, MAX_SCREEN_WIDTH);
       
   427 			h = clamp(h, 64, MAX_SCREEN_HEIGHT);
       
   428 			if (_wnd.double_size) {
       
   429 				w *= 2;
       
   430 				h *= 2;
       
   431 			}
       
   432 			SetRect(&r2, 0, 0, w, h);
       
   433 
       
   434 			AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
       
   435 			w = r2.right - r2.left;
       
   436 			h = r2.bottom - r2.top;
       
   437 
       
   438 			switch (wParam) {
       
   439 				case WMSZ_BOTTOM:
       
   440 					r->bottom = r->top + h;
       
   441 					break;
       
   442 
       
   443 				case WMSZ_BOTTOMLEFT:
       
   444 					r->bottom = r->top + h;
       
   445 					r->left = r->right - w;
       
   446 					break;
       
   447 
       
   448 				case WMSZ_BOTTOMRIGHT:
       
   449 					r->bottom = r->top + h;
       
   450 					r->right = r->left + w;
       
   451 					break;
       
   452 
       
   453 				case WMSZ_LEFT:
       
   454 					r->left = r->right - w;
       
   455 					break;
       
   456 
       
   457 				case WMSZ_RIGHT:
       
   458 					r->right = r->left + w;
       
   459 					break;
       
   460 
       
   461 				case WMSZ_TOP:
       
   462 					r->top = r->bottom - h;
       
   463 					break;
       
   464 
       
   465 				case WMSZ_TOPLEFT:
       
   466 					r->top = r->bottom - h;
       
   467 					r->left = r->right - w;
       
   468 					break;
       
   469 
       
   470 				case WMSZ_TOPRIGHT:
       
   471 					r->top = r->bottom - h;
       
   472 					r->right = r->left + w;
       
   473 					break;
       
   474 			}
       
   475 			return TRUE;
       
   476 		}
       
   477 
       
   478 // needed for wheel
       
   479 #if !defined(WM_MOUSEWHEEL)
       
   480 # define WM_MOUSEWHEEL 0x020A
       
   481 #endif  //WM_MOUSEWHEEL
       
   482 #if !defined(GET_WHEEL_DELTA_WPARAM)
       
   483 # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
       
   484 #endif  //GET_WHEEL_DELTA_WPARAM
       
   485 
       
   486 		case WM_MOUSEWHEEL: {
       
   487 			int delta = GET_WHEEL_DELTA_WPARAM(wParam);
       
   488 
       
   489 			if (delta < 0) {
       
   490 				_cursor.wheel++;
       
   491 			} else if (delta > 0) {
       
   492 				_cursor.wheel--;
       
   493 			}
       
   494 			HandleMouseEvents();
       
   495 			return 0;
       
   496 		}
       
   497 
       
   498 		case WM_ACTIVATEAPP:
       
   499 			_wnd.has_focus = (bool)wParam;
       
   500 			break;
       
   501 	}
       
   502 	return DefWindowProc(hwnd, msg, wParam, lParam);
       
   503 }
       
   504 
       
   505 static void RegisterWndClass(void)
       
   506 {
       
   507 	static bool registered = false;
       
   508 
       
   509 	if (!registered) {
       
   510 		HINSTANCE hinst = GetModuleHandle(NULL);
       
   511 		WNDCLASS wnd = {
       
   512 			0,
       
   513 			WndProcGdi,
       
   514 			0,
       
   515 			0,
       
   516 			hinst,
       
   517 			LoadIcon(hinst, MAKEINTRESOURCE(100)),
       
   518 			LoadCursor(NULL, IDC_ARROW),
       
   519 			0,
       
   520 			0,
       
   521 			_T("OTTD")
       
   522 		};
       
   523 
       
   524 		registered = true;
       
   525 		if (!RegisterClass(&wnd)) error("RegisterClass failed");
       
   526 	}
       
   527 }
       
   528 
       
   529 static void MakeWindow(bool full_screen)
       
   530 {
       
   531 	_fullscreen = full_screen;
       
   532 
       
   533 	_wnd.double_size = _double_size && !full_screen;
       
   534 
       
   535 	// recreate window?
       
   536 	if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) {
       
   537 		DestroyWindow(_wnd.main_wnd);
       
   538 		_wnd.main_wnd = 0;
       
   539 	}
       
   540 
       
   541 	if (full_screen) {
       
   542 		DEVMODE settings;
       
   543 
       
   544 		memset(&settings, 0, sizeof(settings));
       
   545 		settings.dmSize = sizeof(settings);
       
   546 		settings.dmFields =
       
   547 			(_fullscreen_bpp != 0 ? DM_BITSPERPEL : 0) |
       
   548 			DM_PELSWIDTH |
       
   549 			DM_PELSHEIGHT |
       
   550 			(_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0);
       
   551 		settings.dmBitsPerPel = _fullscreen_bpp;
       
   552 		settings.dmPelsWidth  = _wnd.width_org;
       
   553 		settings.dmPelsHeight = _wnd.height_org;
       
   554 		settings.dmDisplayFrequency = _display_hz;
       
   555 
       
   556 		if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
       
   557 			MakeWindow(false);
       
   558 			return;
       
   559 		}
       
   560 	} else if (_wnd.fullscreen) {
       
   561 		// restore display?
       
   562 		ChangeDisplaySettings(NULL, 0);
       
   563 	}
       
   564 
       
   565 	{
       
   566 		RECT r;
       
   567 		DWORD style, showstyle;
       
   568 		int x, y, w, h;
       
   569 
       
   570 		showstyle = SW_SHOWNORMAL;
       
   571 		_wnd.fullscreen = full_screen;
       
   572 		if (_wnd.fullscreen) {
       
   573 			style = WS_POPUP;
       
   574 			SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org);
       
   575 		} else {
       
   576 			style = WS_OVERLAPPEDWINDOW;
       
   577 			/* On window creation, check if we were in maximize mode before */
       
   578 			if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
       
   579 			SetRect(&r, 0, 0, _wnd.width, _wnd.height);
       
   580 		}
       
   581 
       
   582 		AdjustWindowRect(&r, style, FALSE);
       
   583 		w = r.right - r.left;
       
   584 		h = r.bottom - r.top;
       
   585 		x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
       
   586 		y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
       
   587 
       
   588 		if (_wnd.main_wnd) {
       
   589 			ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL); // remove maximize-flag
       
   590 			SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
       
   591 		} else {
       
   592 			extern const char _openttd_revision[];
       
   593 			TCHAR Windowtitle[50];
       
   594 
       
   595 			_sntprintf(Windowtitle, sizeof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision));
       
   596 
       
   597 			_wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0);
       
   598 			if (_wnd.main_wnd == NULL) error("CreateWindow failed");
       
   599 			ShowWindow(_wnd.main_wnd, showstyle);
       
   600 		}
       
   601 	}
       
   602 	GameSizeChanged(); // invalidate all windows, force redraw
       
   603 }
       
   604 
       
   605 static bool AllocateDibSection(int w, int h)
       
   606 {
       
   607 	BITMAPINFO *bi;
       
   608 	HDC dc;
       
   609 
       
   610 	w = clamp(w, 64, MAX_SCREEN_WIDTH);
       
   611 	h = clamp(h, 64, MAX_SCREEN_HEIGHT);
       
   612 
       
   613 	if (w == _screen.width && h == _screen.height)
       
   614 		return false;
       
   615 
       
   616 	_screen.width = w;
       
   617 	_screen.pitch = ALIGN(w, 4);
       
   618 	_screen.height = h;
       
   619 
       
   620 	if (_wnd.alloced_bits) {
       
   621 		free(_wnd.alloced_bits);
       
   622 		_wnd.alloced_bits = NULL;
       
   623 	}
       
   624 
       
   625 	bi = alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
       
   626 	memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
       
   627 	bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
       
   628 
       
   629 	if (_wnd.double_size) {
       
   630 		w = ALIGN(w, 4);
       
   631 		_wnd.alloced_bits = _wnd.buffer_bits = malloc(w * h);
       
   632 		w *= 2;
       
   633 		h *= 2;
       
   634 	}
       
   635 
       
   636 	bi->bmiHeader.biWidth = _wnd.width = w;
       
   637 	bi->bmiHeader.biHeight = -(_wnd.height = h);
       
   638 
       
   639 	bi->bmiHeader.biPlanes = 1;
       
   640 	bi->bmiHeader.biBitCount = 8;
       
   641 	bi->bmiHeader.biCompression = BI_RGB;
       
   642 
       
   643 	if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
       
   644 
       
   645 	dc = GetDC(0);
       
   646 	_wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.bitmap_bits, NULL, 0);
       
   647 	if (_wnd.dib_sect == NULL) error("CreateDIBSection failed");
       
   648 	ReleaseDC(0, dc);
       
   649 
       
   650 	if (!_wnd.double_size) _wnd.buffer_bits = _wnd.bitmap_bits;
       
   651 
       
   652 	return true;
       
   653 }
       
   654 
       
   655 static const uint16 default_resolutions[][2] = {
       
   656 	{  640,  480 },
       
   657 	{  800,  600 },
       
   658 	{ 1024,  768 },
       
   659 	{ 1152,  864 },
       
   660 	{ 1280,  800 },
       
   661 	{ 1280,  960 },
       
   662 	{ 1280, 1024 },
       
   663 	{ 1400, 1050 },
       
   664 	{ 1600, 1200 },
       
   665 	{ 1680, 1050 },
       
   666 	{ 1920, 1200 }
       
   667 };
       
   668 
       
   669 static void FindResolutions(void)
       
   670 {
       
   671 	uint n = 0;
       
   672 	uint i;
       
   673 	DEVMODEA dm;
       
   674 
       
   675 	/* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95
       
   676 	 * Doesn't really matter since we don't pass a string anyways, but still
       
   677 	 * a letdown */
       
   678 	for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) {
       
   679 		if (dm.dmBitsPerPel == 8 && IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) &&
       
   680 				IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)) {
       
   681 			uint j;
       
   682 
       
   683 			for (j = 0; j < n; j++) {
       
   684 				if (_resolutions[j][0] == dm.dmPelsWidth && _resolutions[j][1] == dm.dmPelsHeight) break;
       
   685 			}
       
   686 
       
   687 			/* In the previous loop we have checked already existing/added resolutions if
       
   688 			 * they are the same as the new ones. If this is not the case (j == n); we have
       
   689 			 * looped all and found none, add the new one to the list. If we have reached the
       
   690 			 * maximum amount of resolutions, then quit querying the display */
       
   691 			if (j == n) {
       
   692 				_resolutions[j][0] = dm.dmPelsWidth;
       
   693 				_resolutions[j][1] = dm.dmPelsHeight;
       
   694 				if (++n == lengthof(_resolutions)) break;
       
   695 			}
       
   696 		}
       
   697 	}
       
   698 
       
   699 	/* We have found no resolutions, show the default list */
       
   700 	if (n == 0) {
       
   701 		memcpy(_resolutions, default_resolutions, sizeof(default_resolutions));
       
   702 		n = lengthof(default_resolutions);
       
   703 	}
       
   704 
       
   705 	_num_resolutions = n;
       
   706 	SortResolutions(_num_resolutions);
       
   707 }
       
   708 
       
   709 
       
   710 static const char *Win32GdiStart(const char * const *parm)
       
   711 {
       
   712 	memset(&_wnd, 0, sizeof(_wnd));
       
   713 
       
   714 	RegisterWndClass();
       
   715 
       
   716 	MakePalette();
       
   717 
       
   718 	FindResolutions();
       
   719 
       
   720 	// fullscreen uses those
       
   721 	_wnd.width_org = _cur_resolution[0];
       
   722 	_wnd.height_org = _cur_resolution[1];
       
   723 
       
   724 	AllocateDibSection(_cur_resolution[0], _cur_resolution[1]);
       
   725 	MarkWholeScreenDirty();
       
   726 
       
   727 	MakeWindow(_fullscreen);
       
   728 
       
   729 	return NULL;
       
   730 }
       
   731 
       
   732 static void Win32GdiStop(void)
       
   733 {
       
   734 	DeleteObject(_wnd.gdi_palette);
       
   735 	DeleteObject(_wnd.dib_sect);
       
   736 	DestroyWindow(_wnd.main_wnd);
       
   737 
       
   738 	if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0);
       
   739 	if (_double_size) {
       
   740 		_cur_resolution[0] *= 2;
       
   741 		_cur_resolution[1] *= 2;
       
   742 	}
       
   743 
       
   744 	MyShowCursor(true);
       
   745 }
       
   746 
       
   747 // simple upscaler by 2
       
   748 static void filter(int left, int top, int width, int height)
       
   749 {
       
   750 	uint p = _screen.pitch;
       
   751 	const Pixel *s = _wnd.buffer_bits + top * p + left;
       
   752 	Pixel *d = _wnd.bitmap_bits + top * p * 4 + left * 2;
       
   753 
       
   754 	for (; height > 0; height--) {
       
   755 		int i;
       
   756 
       
   757 		for (i = 0; i != width; i++) {
       
   758 			d[i * 2] = d[i * 2 + 1] = d[i * 2 + p * 2] = d[i * 2 + 1 + p * 2] = s[i];
       
   759 		}
       
   760 		s += p;
       
   761 		d += p * 4;
       
   762 	}
       
   763 }
       
   764 
       
   765 static void Win32GdiMakeDirty(int left, int top, int width, int height)
       
   766 {
       
   767 	RECT r = { left, top, left + width, top + height };
       
   768 
       
   769 	if (_wnd.double_size) {
       
   770 		filter(left, top, width, height);
       
   771 		r.left *= 2;
       
   772 		r.top *= 2;
       
   773 		r.right *= 2;
       
   774 		r.bottom *= 2;
       
   775 	}
       
   776 	InvalidateRect(_wnd.main_wnd, &r, FALSE);
       
   777 }
       
   778 
       
   779 static void CheckPaletteAnim(void)
       
   780 {
       
   781 	if (_pal_last_dirty == -1)
       
   782 		return;
       
   783 	InvalidateRect(_wnd.main_wnd, NULL, FALSE);
       
   784 }
       
   785 
       
   786 static void Win32GdiMainLoop(void)
       
   787 {
       
   788 	MSG mesg;
       
   789 	uint32 cur_ticks = GetTickCount();
       
   790 	uint32 next_tick = cur_ticks + 30;
       
   791 
       
   792 	_wnd.running = true;
       
   793 
       
   794 	for (;;) {
       
   795 		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
       
   796 
       
   797 		while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
       
   798 			InteractiveRandom(); // randomness
       
   799 			DispatchMessage(&mesg);
       
   800 		}
       
   801 		if (_exit_game) return;
       
   802 
       
   803 #if defined(_DEBUG)
       
   804 		if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 &&
       
   805 #else
       
   806 		/* Speed up using TAB, but disable for ALT+TAB of course */
       
   807 		if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 &&
       
   808 #endif
       
   809 			  !_networking && _game_mode != GM_MENU) {
       
   810 			_fast_forward |= 2;
       
   811 		} else if (_fast_forward & 2) {
       
   812 			_fast_forward = 0;
       
   813 		}
       
   814 
       
   815 		cur_ticks = GetTickCount();
       
   816 		if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) {
       
   817 			next_tick = cur_ticks + 30;
       
   818 			_ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0;
       
   819 			_shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0;
       
   820 #ifdef _DEBUG
       
   821 			_dbg_screen_rect = _wnd.has_focus && GetAsyncKeyState(VK_CAPITAL)<0;
       
   822 #endif
       
   823 
       
   824 			// determine which directional keys are down
       
   825 			if (_wnd.has_focus) {
       
   826 				_dirkeys =
       
   827 					(GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
       
   828 					(GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
       
   829 					(GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
       
   830 					(GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
       
   831 			} else {
       
   832 				_dirkeys = 0;
       
   833 			}
       
   834 
       
   835 			GameLoop();
       
   836 			_cursor.delta.x = _cursor.delta.y = 0;
       
   837 
       
   838 			if (_force_full_redraw) MarkWholeScreenDirty();
       
   839 
       
   840 			GdiFlush();
       
   841 			_screen.dst_ptr = _wnd.buffer_bits;
       
   842 			UpdateWindows();
       
   843 			CheckPaletteAnim();
       
   844 		} else {
       
   845 			Sleep(1);
       
   846 			GdiFlush();
       
   847 			_screen.dst_ptr = _wnd.buffer_bits;
       
   848 			DrawTextMessage();
       
   849 			DrawMouseCursor();
       
   850 		}
       
   851 	}
       
   852 }
       
   853 
       
   854 static bool Win32GdiChangeRes(int w, int h)
       
   855 {
       
   856 	_wnd.width = _wnd.width_org = w;
       
   857 	_wnd.height = _wnd.height_org = h;
       
   858 
       
   859 	MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching
       
   860 
       
   861 	return true;
       
   862 }
       
   863 
       
   864 static void Win32GdiFullScreen(bool full_screen)
       
   865 {
       
   866 	MakeWindow(full_screen);
       
   867 }
       
   868 
       
   869 const HalVideoDriver _win32_video_driver = {
       
   870 	Win32GdiStart,
       
   871 	Win32GdiStop,
       
   872 	Win32GdiMakeDirty,
       
   873 	Win32GdiMainLoop,
       
   874 	Win32GdiChangeRes,
       
   875 	Win32GdiFullScreen,
       
   876 };