tron@2186: /* $Id$ */ tron@2186: tron@2189: #include "../stdafx.h" tron@2189: #include "../openttd.h" tron@2189: #include "../functions.h" tron@2189: #include "../gfx.h" tron@2189: #include "../macros.h" tron@2189: #include "../network.h" tron@2189: #include "../variables.h" tron@2207: #include "../win32.h" tron@2189: #include "../window.h" tron@2189: #include "win32_v.h" tron@2174: #include tron@2174: tron@2174: static struct { tron@2174: HWND main_wnd; tron@2174: HBITMAP dib_sect; tron@2174: Pixel *bitmap_bits; tron@2174: Pixel *buffer_bits; tron@2174: Pixel *alloced_bits; tron@2174: HPALETTE gdi_palette; tron@2174: int width,height; tron@2174: int width_org, height_org; tron@2174: bool fullscreen; tron@2174: bool double_size; tron@2174: bool has_focus; tron@2174: bool running; tron@2174: } _wnd; tron@2174: Darkvater@3051: bool _force_full_redraw; Darkvater@3051: bool _double_size; Darkvater@3051: uint _display_hz; Darkvater@3051: uint _fullscreen_bpp; Darkvater@3051: tron@2174: static void MakePalette(void) tron@2174: { tron@2174: LOGPALETTE *pal; tron@2174: uint i; tron@2174: tron@2174: pal = alloca(sizeof(LOGPALETTE) + (256-1) * sizeof(PALETTEENTRY)); tron@2174: tron@2174: pal->palVersion = 0x300; tron@2174: pal->palNumEntries = 256; tron@2174: tron@2174: for (i = 0; i != 256; i++) { tron@2174: pal->palPalEntry[i].peRed = _cur_palette[i].r; tron@2174: pal->palPalEntry[i].peGreen = _cur_palette[i].g; tron@2174: pal->palPalEntry[i].peBlue = _cur_palette[i].b; tron@2174: pal->palPalEntry[i].peFlags = 0; tron@2174: tron@2174: } tron@2174: _wnd.gdi_palette = CreatePalette(pal); tron@2174: if (_wnd.gdi_palette == NULL) tron@2174: error("CreatePalette failed!\n"); tron@2174: } tron@2174: tron@2174: static void UpdatePalette(HDC dc, uint start, uint count) tron@2174: { tron@2174: RGBQUAD rgb[256]; tron@2174: uint i; tron@2174: tron@2174: for (i = 0; i != count; i++) { tron@2174: rgb[i].rgbRed = _cur_palette[start + i].r; tron@2174: rgb[i].rgbGreen = _cur_palette[start + i].g; tron@2174: rgb[i].rgbBlue = _cur_palette[start + i].b; tron@2174: rgb[i].rgbReserved = 0; tron@2174: } tron@2174: tron@2174: SetDIBColorTable(dc, start, count, rgb); tron@2174: } tron@2174: tron@2174: typedef struct { tron@2174: byte vk_from; tron@2174: byte vk_count; tron@2174: byte map_to; tron@2174: } VkMapping; tron@2174: tron@2174: #define AS(x, z) {x, 0, z} tron@2174: #define AM(x, y, z, w) {x, y - x, z} tron@2174: tron@2174: #ifndef VK_OEM_3 tron@2174: #define VK_OEM_3 0xC0 tron@2174: #endif tron@2174: tron@2174: static const VkMapping _vk_mapping[] = { tron@2174: // Pageup stuff + up/down tron@2174: AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN), tron@2174: // Map letters & digits tron@2174: AM('A','Z','A','Z'), tron@2174: AM('0','9','0','9'), tron@2174: tron@2174: AS(VK_ESCAPE, WKC_ESC), tron@2174: AS(VK_PAUSE, WKC_PAUSE), tron@2174: AS(VK_BACK, WKC_BACKSPACE), tron@2174: AM(VK_INSERT,VK_DELETE,WKC_INSERT, WKC_DELETE), tron@2174: tron@2174: AS(VK_SPACE, WKC_SPACE), tron@2174: AS(VK_RETURN, WKC_RETURN), tron@2174: AS(VK_TAB, WKC_TAB), tron@2174: tron@2174: // Function keys tron@2174: AM(VK_F1, VK_F12, WKC_F1, WKC_F12), tron@2174: tron@2174: // Numeric part. tron@2174: // What is the virtual keycode for numeric enter?? tron@2174: AM(VK_NUMPAD0,VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9), tron@2174: AS(VK_DIVIDE, WKC_NUM_DIV), tron@2174: AS(VK_MULTIPLY, WKC_NUM_MUL), tron@2174: AS(VK_SUBTRACT, WKC_NUM_MINUS), tron@2174: AS(VK_ADD, WKC_NUM_PLUS), tron@2174: AS(VK_DECIMAL, WKC_NUM_DECIMAL) tron@2174: }; tron@2174: tron@2174: static uint MapWindowsKey(uint sym) tron@2174: { tron@2174: const VkMapping *map; tron@2174: uint key = 0; tron@2174: tron@2174: for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { tron@2174: if ((uint)(sym - map->vk_from) <= map->vk_count) { tron@2174: key = sym - map->vk_from + map->map_to; tron@2174: break; tron@2174: } tron@2174: } tron@2174: tron@2174: if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT; tron@2174: if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL; tron@2174: if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT; tron@2174: return key; tron@2174: } tron@2174: tron@2174: static void MakeWindow(bool full_screen); tron@2174: static bool AllocateDibSection(int w, int h); tron@2174: tron@2174: static void ClientSizeChanged(int w, int h) tron@2174: { tron@2174: if (_wnd.double_size) { tron@2174: w /= 2; tron@2174: h /= 2; tron@2174: } tron@2174: tron@2174: // allocate new dib section of the new size tron@2174: if (AllocateDibSection(w, h)) { tron@2174: // mark all palette colors dirty tron@2174: _pal_first_dirty = 0; tron@2174: _pal_last_dirty = 255; tron@2174: GameSizeChanged(); tron@2174: tron@2174: // redraw screen tron@2174: if (_wnd.running) { tron@2174: _screen.dst_ptr = _wnd.buffer_bits; tron@2174: UpdateWindows(); tron@2174: } tron@2174: } tron@2174: } tron@2174: tron@2174: extern void DoExitSave(void); tron@2174: tron@2174: #ifdef _DEBUG tron@2174: // Keep this function here.. tron@2174: // It allows you to redraw the screen from within the MSVC debugger tron@2174: int RedrawScreenDebug(void) tron@2174: { tron@2174: HDC dc,dc2; tron@2174: static int _fooctr; tron@2174: HBITMAP old_bmp; tron@2174: HPALETTE old_palette; tron@2174: tron@2174: _screen.dst_ptr = _wnd.buffer_bits; tron@2174: UpdateWindows(); tron@2174: tron@2174: dc = GetDC(_wnd.main_wnd); tron@2174: dc2 = CreateCompatibleDC(dc); tron@2174: tron@2174: old_bmp = SelectObject(dc2, _wnd.dib_sect); tron@2174: old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); tron@2174: BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); tron@2174: SelectPalette(dc, old_palette, TRUE); tron@2174: SelectObject(dc2, old_bmp); tron@2174: DeleteDC(dc2); tron@2174: ReleaseDC(_wnd.main_wnd, dc); tron@2174: tron@2174: return _fooctr++; tron@2174: } tron@2174: #endif tron@2174: tron@2174: static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) tron@2174: { tron@2174: switch (msg) { tron@2174: case WM_PAINT: { tron@2174: PAINTSTRUCT ps; tron@2174: HDC dc,dc2; tron@2174: HBITMAP old_bmp; tron@2174: HPALETTE old_palette; tron@2174: BeginPaint(hwnd, &ps); tron@2174: dc = ps.hdc; tron@2174: dc2 = CreateCompatibleDC(dc); tron@2174: old_bmp = SelectObject(dc2, _wnd.dib_sect); tron@2174: old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); tron@2174: tron@2174: if (_pal_last_dirty != -1) { tron@2174: UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); tron@2174: _pal_last_dirty = -1; tron@2174: } tron@2174: tron@2174: BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); tron@2174: SelectPalette(dc, old_palette, TRUE); tron@2174: SelectObject(dc2, old_bmp); tron@2174: DeleteDC(dc2); tron@2174: EndPaint(hwnd, &ps); tron@2174: } tron@2174: return 0; tron@2174: tron@2174: case WM_PALETTECHANGED: tron@2174: if ((HWND)wParam == hwnd) tron@2174: return 0; tron@2174: // FALL THROUGH tron@2174: case WM_QUERYNEWPALETTE: { tron@2174: HDC hDC = GetWindowDC(hwnd); tron@2174: HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE); tron@2174: UINT nChanged = RealizePalette(hDC); tron@2174: SelectPalette(hDC, hOldPalette, TRUE); tron@2174: ReleaseDC(hwnd, hDC); tron@2174: if (nChanged) tron@2174: InvalidateRect(hwnd, NULL, FALSE); tron@2174: return 0; tron@2174: } tron@2174: tron@2174: case WM_CLOSE: tron@2174: if (_game_mode == GM_MENU) { // do not ask to quit on the main screen tron@2174: _exit_game = true; tron@2174: } else if (_patches.autosave_on_exit) { tron@2174: DoExitSave(); tron@2174: _exit_game = true; tron@2174: } else tron@2174: AskExitGame(); tron@2174: tron@2174: return 0; tron@2174: tron@2174: case WM_LBUTTONDOWN: tron@2174: SetCapture(hwnd); tron@2174: _left_button_down = true; tron@2174: return 0; tron@2174: tron@2174: case WM_LBUTTONUP: tron@2174: ReleaseCapture(); tron@2174: _left_button_down = false; tron@2174: _left_button_clicked = false; tron@2174: return 0; tron@2174: tron@2174: case WM_RBUTTONDOWN: tron@2174: SetCapture(hwnd); tron@2174: _right_button_down = true; tron@2174: _right_button_clicked = true; tron@2174: return 0; tron@2174: tron@2174: case WM_RBUTTONUP: tron@2174: ReleaseCapture(); tron@2174: _right_button_down = false; tron@2174: return 0; tron@2174: tron@2174: case WM_MOUSEMOVE: { tron@2174: int x = (int16)LOWORD(lParam); tron@2174: int y = (int16)HIWORD(lParam); tron@2174: POINT pt; tron@2174: tron@2174: if (_wnd.double_size) { tron@2174: x /= 2; tron@2174: y /= 2; tron@2174: } tron@2174: tron@2174: if (_cursor.fix_at) { tron@2174: int dx = x - _cursor.pos.x; tron@2174: int dy = y - _cursor.pos.y; tron@2174: if (dx != 0 || dy != 0) { tron@2174: _cursor.delta.x += dx; tron@2174: _cursor.delta.y += dy; tron@2174: tron@2174: pt.x = _cursor.pos.x; tron@2174: pt.y = _cursor.pos.y; tron@2174: tron@2174: if (_wnd.double_size) { tron@2174: pt.x *= 2; tron@2174: pt.y *= 2; tron@2174: } tron@2174: ClientToScreen(hwnd, &pt); tron@2174: SetCursorPos(pt.x, pt.y); tron@2174: } tron@2174: } else { tron@2174: _cursor.delta.x += x - _cursor.pos.x; tron@2174: _cursor.delta.y += y - _cursor.pos.y; tron@2174: _cursor.pos.x = x; tron@2174: _cursor.pos.y = y; tron@2174: _cursor.dirty = true; tron@2174: } tron@2174: MyShowCursor(false); tron@2174: return 0; tron@2174: } tron@2174: tron@2174: case WM_KEYDOWN: { tron@2174: // this is the rewritten ascii input function tron@2174: // it disables windows deadkey handling --> more linux like :D tron@2174: unsigned short w = 0; tron@2174: int r = 0; tron@2174: byte ks[256]; tron@2174: unsigned int scan = 0; tron@2174: uint16 scancode = (( lParam & 0xFF0000 ) >> 16 ); tron@2174: tron@2174: GetKeyboardState(ks); tron@2174: r = ToAscii(wParam, scan, ks, &w, 0); tron@2174: if (r == 0) w = 0; // no translation was possible tron@2174: tron@2174: _pressed_key = w | MapWindowsKey(wParam) << 16; tron@2174: tron@2174: if (scancode == 41) tron@2174: _pressed_key = w | WKC_BACKQUOTE << 16; tron@2174: tron@2174: if ((_pressed_key >> 16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) { tron@2174: _double_size ^= 1; tron@2174: _wnd.double_size = _double_size; tron@2174: ClientSizeChanged(_wnd.width, _wnd.height); tron@2174: MarkWholeScreenDirty(); tron@2174: } tron@2174: } break; tron@2174: tron@2174: case WM_SYSKEYDOWN: /* user presses F10 or Alt, both activating the title-menu */ tron@2174: switch (wParam) { tron@2174: case VK_RETURN: case 0x46: /* Full Screen on ALT + ENTER/F(VK_F) */ tron@2174: ToggleFullScreen(!_wnd.fullscreen); tron@2174: return 0; tron@2174: case VK_MENU: /* Just ALT */ tron@2174: return 0; // do nothing tron@2174: case VK_F10: /* F10, ignore activation of menu */ tron@2174: _pressed_key = MapWindowsKey(wParam) << 16; tron@2174: return 0; tron@2174: default: /* ALT in combination with something else */ tron@2174: _pressed_key = MapWindowsKey(wParam) << 16; tron@2174: break; tron@2174: } tron@2174: break; tron@2174: case WM_NCMOUSEMOVE: tron@2174: MyShowCursor(true); tron@2174: return 0; tron@2174: tron@2174: case WM_SIZE: { tron@2174: if (wParam != SIZE_MINIMIZED) { tron@2174: ClientSizeChanged(LOWORD(lParam), HIWORD(lParam)); tron@2174: } tron@2174: return 0; tron@2174: } tron@2174: case WM_SIZING: { tron@2174: RECT* r = (RECT*)lParam; tron@2174: RECT r2; tron@2174: int w, h; tron@2174: tron@2174: SetRect(&r2, 0, 0, 0, 0); tron@2174: AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); tron@2174: tron@2174: w = r->right - r->left - (r2.right - r2.left); tron@2174: h = r->bottom - r->top - (r2.bottom - r2.top); tron@2174: if (_wnd.double_size) { tron@2174: w /= 2; tron@2174: h /= 2; tron@2174: } tron@2174: w = clamp(w, 64, MAX_SCREEN_WIDTH); tron@2174: h = clamp(h, 64, MAX_SCREEN_HEIGHT); tron@2174: if (_wnd.double_size) { tron@2174: w *= 2; tron@2174: h *= 2; tron@2174: } tron@2174: SetRect(&r2, 0, 0, w, h); tron@2174: tron@2174: AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); tron@2174: w = r2.right - r2.left; tron@2174: h = r2.bottom - r2.top; tron@2174: tron@2174: switch (wParam) { tron@2174: case WMSZ_BOTTOM: tron@2174: r->bottom = r->top + h; tron@2174: break; tron@2174: case WMSZ_BOTTOMLEFT: tron@2174: r->bottom = r->top + h; tron@2174: r->left = r->right - w; tron@2174: break; tron@2174: case WMSZ_BOTTOMRIGHT: tron@2174: r->bottom = r->top + h; tron@2174: r->right = r->left + w; tron@2174: break; tron@2174: case WMSZ_LEFT: tron@2174: r->left = r->right - w; tron@2174: break; tron@2174: case WMSZ_RIGHT: tron@2174: r->right = r->left + w; tron@2174: break; tron@2174: case WMSZ_TOP: tron@2174: r->top = r->bottom - h; tron@2174: break; tron@2174: case WMSZ_TOPLEFT: tron@2174: r->top = r->bottom - h; tron@2174: r->left = r->right - w; tron@2174: break; tron@2174: case WMSZ_TOPRIGHT: tron@2174: r->top = r->bottom - h; tron@2174: r->right = r->left + w; tron@2174: break; tron@2174: } tron@2174: return TRUE; tron@2174: } tron@2174: tron@2174: // needed for wheel tron@2174: #if !defined(WM_MOUSEWHEEL) tron@2174: # define WM_MOUSEWHEEL 0x020A tron@2174: #endif //WM_MOUSEWHEEL tron@2174: #if !defined(GET_WHEEL_DELTA_WPARAM) tron@2174: # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam)) tron@2174: #endif //GET_WHEEL_DELTA_WPARAM tron@2174: tron@2174: case WM_MOUSEWHEEL: { tron@2174: int delta = GET_WHEEL_DELTA_WPARAM(wParam); tron@2174: tron@2174: if (delta < 0) { tron@2174: _cursor.wheel++; tron@2174: } else if (delta > 0) { tron@2174: _cursor.wheel--; tron@2174: } tron@2174: return 0; tron@2174: } tron@2174: tron@2174: case WM_ACTIVATEAPP: tron@2174: _wnd.has_focus = (bool)wParam; tron@2174: break; tron@2174: } tron@2174: return DefWindowProc(hwnd, msg, wParam, lParam); tron@2174: } tron@2174: tron@2174: static void RegisterWndClass(void) tron@2174: { tron@2174: static bool registered; tron@2174: if (!registered) { tron@2174: HINSTANCE hinst = GetModuleHandle(NULL); tron@2174: WNDCLASS wnd = { tron@2174: 0, tron@2174: WndProcGdi, tron@2174: 0, tron@2174: 0, tron@2174: hinst, tron@2174: LoadIcon(hinst, MAKEINTRESOURCE(100)), tron@2174: LoadCursor(NULL, IDC_ARROW), tron@2174: 0, tron@2174: 0, tron@2174: "OTTD" tron@2174: }; tron@2174: registered = true; tron@2174: if (!RegisterClass(&wnd)) tron@2174: error("RegisterClass failed"); tron@2174: } tron@2174: } tron@2174: tron@2174: extern const char _openttd_revision[]; tron@2174: tron@2174: static void MakeWindow(bool full_screen) tron@2174: { tron@2174: _fullscreen = full_screen; tron@2174: tron@2174: _wnd.double_size = _double_size && !full_screen; tron@2174: tron@2174: // recreate window? tron@2174: if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) { tron@2174: DestroyWindow(_wnd.main_wnd); tron@2174: _wnd.main_wnd = 0; tron@2174: } tron@2174: tron@2174: if (full_screen) { tron@2174: DEVMODE settings; tron@2174: memset(&settings, 0, sizeof(DEVMODE)); tron@2174: settings.dmSize = sizeof(DEVMODE); tron@2174: settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; tron@2174: tron@2174: if (_fullscreen_bpp) { tron@2174: settings.dmBitsPerPel = _fullscreen_bpp; tron@2174: settings.dmFields |= DM_BITSPERPEL; tron@2174: } tron@2174: settings.dmPelsWidth = _wnd.width_org; tron@2174: settings.dmPelsHeight = _wnd.height_org; tron@2174: settings.dmDisplayFrequency = _display_hz; tron@2174: if (settings.dmDisplayFrequency != 0) tron@2174: settings.dmFields |= DM_DISPLAYFREQUENCY; tron@2174: if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { tron@2174: MakeWindow(false); tron@2174: return; tron@2174: } tron@2174: } else if (_wnd.fullscreen) { tron@2174: // restore display? tron@2174: ChangeDisplaySettings(NULL, 0); tron@2174: } tron@2174: tron@2174: { tron@2174: RECT r; tron@2174: uint style; tron@2174: int x, y, w, h; tron@2174: tron@2174: _wnd.fullscreen = full_screen; tron@2174: if (_wnd.fullscreen) { tron@2174: style = WS_POPUP | WS_VISIBLE; tron@2174: SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org); tron@2174: } else { tron@2174: style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; tron@2174: SetRect(&r, 0, 0, _wnd.width, _wnd.height); tron@2174: } tron@2174: tron@2174: AdjustWindowRect(&r, style, FALSE); tron@2174: w = r.right - r.left; tron@2174: h = r.bottom - r.top; tron@2174: x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; tron@2174: y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; tron@2174: tron@2174: if (_wnd.main_wnd) { tron@2174: SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); tron@2174: } else { tron@2174: char Windowtitle[50]; tron@2174: tron@2174: snprintf(Windowtitle, lengthof(Windowtitle), "OpenTTD %s", _openttd_revision); tron@2174: tron@2174: _wnd.main_wnd = CreateWindow("OTTD", Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); tron@2174: if (_wnd.main_wnd == NULL) tron@2174: error("CreateWindow failed"); tron@2174: } tron@2174: } tron@2174: GameSizeChanged(); // invalidate all windows, force redraw tron@2174: } tron@2174: tron@2174: static bool AllocateDibSection(int w, int h) tron@2174: { tron@2174: BITMAPINFO *bi; tron@2174: HDC dc; tron@2174: tron@2174: w = clamp(w, 64, MAX_SCREEN_WIDTH); tron@2174: h = clamp(h, 64, MAX_SCREEN_HEIGHT); tron@2174: tron@2174: if (w == _screen.width && h == _screen.height) tron@2174: return false; tron@2174: tron@2174: _screen.width = w; tron@2398: _screen.pitch = ALIGN(w, 4); tron@2174: _screen.height = h; tron@2174: tron@2174: if (_wnd.alloced_bits) { tron@2174: free(_wnd.alloced_bits); tron@2174: _wnd.alloced_bits = NULL; tron@2174: } tron@2174: tron@2174: bi = alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); tron@2174: memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); tron@2174: bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); tron@2174: tron@2174: if (_wnd.double_size) { tron@2398: w = ALIGN(w, 4); tron@2174: _wnd.alloced_bits = _wnd.buffer_bits = malloc(w * h); tron@2174: w *= 2; tron@2174: h *= 2; tron@2174: } tron@2174: tron@2174: bi->bmiHeader.biWidth = _wnd.width = w; tron@2174: bi->bmiHeader.biHeight = -(_wnd.height = h); tron@2174: tron@2174: bi->bmiHeader.biPlanes = 1; tron@2174: bi->bmiHeader.biBitCount = 8; tron@2174: bi->bmiHeader.biCompression = BI_RGB; tron@2174: tron@2174: if (_wnd.dib_sect) tron@2174: DeleteObject(_wnd.dib_sect); tron@2174: tron@2174: dc = GetDC(0); tron@2174: _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (void**)&_wnd.bitmap_bits, NULL, 0); tron@2174: if (_wnd.dib_sect == NULL) tron@2174: error("CreateDIBSection failed"); tron@2174: ReleaseDC(0, dc); tron@2174: tron@2174: if (!_wnd.double_size) tron@2174: _wnd.buffer_bits = _wnd.bitmap_bits; tron@2174: tron@2174: return true; tron@2174: } tron@2174: tron@2174: static const uint16 default_resolutions[][2] = { tron@2174: { 640, 480}, tron@2174: { 800, 600}, tron@2174: {1024, 768}, tron@2174: {1152, 864}, tron@2174: {1280, 800}, tron@2174: {1280, 960}, tron@2174: {1280, 1024}, tron@2174: {1400, 1050}, tron@2174: {1600, 1200}, tron@2174: {1680, 1050}, tron@2174: {1920, 1200} tron@2174: }; tron@2174: tron@2174: static void FindResolutions(void) tron@2174: { tron@2174: int i = 0, n = 0; tron@2174: DEVMODE dm; tron@2174: tron@2174: while (EnumDisplaySettings(NULL, i++, &dm) != 0) { tron@2174: if (dm.dmBitsPerPel == 8 && IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) && tron@2174: IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)){ tron@2174: int j; tron@2174: for (j = 0; j < n; j++) { tron@2174: if (_resolutions[j][0] == dm.dmPelsWidth && _resolutions[j][1] == dm.dmPelsHeight) break; tron@2174: } tron@2174: tron@2174: /* In the previous loop we have checked already existing/added resolutions if tron@2174: * they are the same as the new ones. If this is not the case (j == n); we have tron@2174: * looped all and found none, add the new one to the list. If we have reached the tron@2174: * maximum amount of resolutions, then quit querying the display */ tron@2174: if (j == n) { tron@2174: _resolutions[j][0] = dm.dmPelsWidth; tron@2174: _resolutions[j][1] = dm.dmPelsHeight; tron@2174: if (++n == lengthof(_resolutions)) break; tron@2174: } tron@2174: } tron@2174: } tron@2174: tron@2174: /* We have found no resolutions, show the default list */ tron@2174: if (n == 0) { tron@2174: memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); tron@2174: n = lengthof(default_resolutions); tron@2174: } tron@2174: tron@2174: _num_resolutions = n; tron@2174: SortResolutions(_num_resolutions); tron@2174: } tron@2174: tron@2174: tron@2174: static const char *Win32GdiStart(const char * const *parm) tron@2174: { tron@2174: memset(&_wnd, 0, sizeof(_wnd)); tron@2174: tron@2174: RegisterWndClass(); tron@2174: tron@2174: MakePalette(); tron@2174: tron@2174: FindResolutions(); tron@2174: tron@2174: // fullscreen uses those tron@2174: _wnd.width_org = _cur_resolution[0]; tron@2174: _wnd.height_org = _cur_resolution[1]; tron@2174: tron@2174: AllocateDibSection(_cur_resolution[0], _cur_resolution[1]); tron@2174: MarkWholeScreenDirty(); tron@2174: tron@2174: MakeWindow(_fullscreen); tron@2174: tron@2174: return NULL; tron@2174: } tron@2174: tron@2174: static void Win32GdiStop(void) tron@2174: { tron@2174: if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0); Darkvater@2773: if (_double_size) { Darkvater@2773: _cur_resolution[0] *= 2; Darkvater@2773: _cur_resolution[1] *= 2; Darkvater@2773: } Darkvater@2773: tron@2174: MyShowCursor(true); tron@2174: DeleteObject(_wnd.gdi_palette); tron@2174: DeleteObject(_wnd.dib_sect); tron@2174: DestroyWindow(_wnd.main_wnd); tron@2174: } tron@2174: tron@2174: // simple upscaler by 2 tron@2174: static void filter(int left, int top, int width, int height) tron@2174: { tron@2174: uint p = _screen.pitch; tron@2174: const Pixel *s = _wnd.buffer_bits + top * p + left; tron@2174: Pixel *d = _wnd.bitmap_bits + top * p * 4 + left * 2; tron@2174: tron@2174: for (; height > 0; height--) { tron@2174: int i; tron@2174: tron@2174: for (i = 0; i != width; i++) { tron@2174: d[i * 2] = d[i * 2 + 1] = d[i * 2 + p * 2] = d[i * 2 + 1 + p * 2] = s[i]; tron@2174: } tron@2174: s += p; tron@2174: d += p * 4; tron@2174: } tron@2174: } tron@2174: tron@2174: static void Win32GdiMakeDirty(int left, int top, int width, int height) tron@2174: { tron@2174: RECT r = { left, top, left + width, top + height }; tron@2174: tron@2174: if (_wnd.double_size) { tron@2174: filter(left, top, width, height); tron@2174: r.left *= 2; tron@2174: r.top *= 2; tron@2174: r.right *= 2; tron@2174: r.bottom *= 2; tron@2174: } tron@2174: InvalidateRect(_wnd.main_wnd, &r, FALSE); tron@2174: } tron@2174: tron@2174: static void CheckPaletteAnim(void) tron@2174: { tron@2174: if (_pal_last_dirty == -1) tron@2174: return; tron@2174: InvalidateRect(_wnd.main_wnd, NULL, FALSE); tron@2174: } tron@2174: tron@2228: static void Win32GdiMainLoop(void) tron@2174: { tron@2174: MSG mesg; tron@2174: uint32 next_tick = GetTickCount() + 30, cur_ticks; tron@2174: tron@2174: _wnd.running = true; tron@2174: tron@2952: for (;;) { tron@2174: while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) { tron@2174: InteractiveRandom(); // randomness tron@2174: TranslateMessage(&mesg); tron@2174: DispatchMessage(&mesg); tron@2174: } tron@2228: if (_exit_game) return; tron@2174: tron@2174: #if defined(_DEBUG) tron@2174: if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0) { tron@2174: if ( tron@2174: #else tron@2174: if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0) { tron@2174: /* Disable speeding up game with ALT+TAB (if syskey is pressed, the tron@2174: * real key is in the upper 16 bits (see WM_SYSKEYDOWN in WndProcGdi()) */ tron@2174: if ((_pressed_key >> 16) & WKC_TAB && tron@2174: #endif tron@2174: !_networking && _game_mode != GM_MENU) tron@2174: _fast_forward |= 2; tron@2174: } else if (_fast_forward & 2) tron@2174: _fast_forward = 0; tron@2174: tron@2174: cur_ticks = GetTickCount(); tron@2174: if ((_fast_forward && !_pause) || cur_ticks > next_tick) tron@2174: next_tick = cur_ticks; tron@2174: tron@2174: if (cur_ticks == next_tick) { tron@2174: next_tick += 30; tron@2174: _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; tron@2174: _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; tron@2664: #ifdef _DEBUG tron@2174: _dbg_screen_rect = _wnd.has_focus && GetAsyncKeyState(VK_CAPITAL)<0; tron@2665: #endif tron@2174: tron@2174: // determine which directional keys are down tron@2174: if (_wnd.has_focus) { tron@2174: _dirkeys = tron@2174: (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) + tron@2174: (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) + tron@2174: (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) + tron@2174: (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0); tron@2174: } else tron@2174: _dirkeys = 0; tron@2174: tron@2174: GameLoop(); tron@2174: _cursor.delta.x = _cursor.delta.y = 0; tron@2174: tron@2174: if (_force_full_redraw) tron@2174: MarkWholeScreenDirty(); tron@2174: tron@2174: GdiFlush(); tron@2174: _screen.dst_ptr = _wnd.buffer_bits; tron@2174: UpdateWindows(); tron@2174: CheckPaletteAnim(); tron@2174: } else { tron@2174: Sleep(1); tron@2174: GdiFlush(); tron@2174: _screen.dst_ptr = _wnd.buffer_bits; tron@2174: DrawTextMessage(); tron@2174: DrawMouseCursor(); tron@2174: } tron@2174: } tron@2174: } tron@2174: tron@2174: static bool Win32GdiChangeRes(int w, int h) tron@2174: { tron@2174: _wnd.width = _wnd.width_org = w; tron@2174: _wnd.height = _wnd.height_org = h; tron@2174: tron@2174: MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching tron@2174: tron@2174: return true; tron@2174: } tron@2174: tron@2174: static void Win32GdiFullScreen(bool full_screen) {MakeWindow(full_screen);} tron@2174: tron@2174: const HalVideoDriver _win32_video_driver = { tron@2174: Win32GdiStart, tron@2174: Win32GdiStop, tron@2174: Win32GdiMakeDirty, tron@2174: Win32GdiMainLoop, tron@2174: Win32GdiChangeRes, tron@2174: Win32GdiFullScreen, tron@2174: };