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