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" rubidium@5469: #include "../network/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 Darkvater@5168: #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@4489: int width; tron@4489: int height; tron@4489: int width_org; tron@4489: int 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@4258: bool _window_maximize; Darkvater@3051: uint _display_hz; Darkvater@3051: uint _fullscreen_bpp; Darkvater@5020: static uint16 _bck_resolution[2]; Darkvater@3051: tron@2174: static void MakePalette(void) tron@2174: { tron@2174: LOGPALETTE *pal; tron@2174: uint i; tron@2174: rubidium@5587: pal = (LOGPALETTE*)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@4489: if (_wnd.gdi_palette == NULL) 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: 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: rubidium@4434: AS(VK_ESCAPE, WKC_ESC), rubidium@4434: AS(VK_PAUSE, WKC_PAUSE), rubidium@4434: AS(VK_BACK, WKC_BACKSPACE), rubidium@4434: AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE), tron@2174: rubidium@4434: AS(VK_SPACE, WKC_SPACE), rubidium@4434: AS(VK_RETURN, WKC_RETURN), rubidium@4434: AS(VK_TAB, WKC_TAB), tron@2174: tron@2174: // Function keys rubidium@4434: 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?? rubidium@4434: AM(VK_NUMPAD0, VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9), rubidium@4434: AS(VK_DIVIDE, WKC_NUM_DIV), rubidium@4434: AS(VK_MULTIPLY, WKC_NUM_MUL), rubidium@4434: AS(VK_SUBTRACT, WKC_NUM_MINUS), rubidium@4434: AS(VK_ADD, WKC_NUM_PLUS), rubidium@4434: 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 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: #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: rubidium@5587: old_bmp = (HBITMAP)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: Darkvater@5021: /* Windows 95 will not have a WM_MOUSELEAVE message, so define it if needed */ Darkvater@3312: #if !defined(WM_MOUSELEAVE) Darkvater@3312: #define WM_MOUSELEAVE 0x02A3 Darkvater@3312: #endif Darkvater@3312: #define TID_POLLMOUSE 1 Darkvater@3312: #define MOUSE_POLL_DELAY 75 Darkvater@3312: Darkvater@3312: static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time) Darkvater@3312: { Darkvater@3312: RECT rc; Darkvater@3312: POINT pt; Darkvater@3312: Darkvater@3312: /* Get the rectangle of our window and translate it to screen coordinates. Darkvater@3312: * Compare this with the current screen coordinates of the mouse and if it Darkvater@3312: * falls outside of the area or our window we have left the window. */ Darkvater@3312: GetClientRect(hwnd, &rc); glx@3802: MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2); Darkvater@3312: GetCursorPos(&pt); Darkvater@3312: Darkvater@3312: if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) { Darkvater@3312: KillTimer(hwnd, event); Darkvater@3312: PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L); Darkvater@3312: } Darkvater@3312: } Darkvater@3312: tron@2174: static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) tron@2174: { tron@2174: switch (msg) { tron@4489: case WM_CREATE: tron@4489: SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); tron@4489: break; tron@2174: tron@4489: case WM_PAINT: { tron@4489: PAINTSTRUCT ps; tron@4489: HDC dc,dc2; tron@4489: HBITMAP old_bmp; tron@4489: HPALETTE old_palette; tron@2174: tron@4489: BeginPaint(hwnd, &ps); tron@4489: dc = ps.hdc; tron@4489: dc2 = CreateCompatibleDC(dc); rubidium@5587: old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect); tron@4489: old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); Darkvater@3312: tron@4489: if (_pal_last_dirty != -1) { tron@4489: UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); tron@4489: _pal_last_dirty = -1; tron@4489: } tron@2174: tron@4489: BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); tron@4489: SelectPalette(dc, old_palette, TRUE); tron@4489: SelectObject(dc2, old_bmp); tron@4489: DeleteDC(dc2); tron@4489: EndPaint(hwnd, &ps); tron@4489: return 0; tron@2174: } tron@2174: tron@4489: case WM_PALETTECHANGED: tron@4489: if ((HWND)wParam == hwnd) return 0; tron@4489: /* FALLTHROUGH */ tron@2174: tron@4489: case WM_QUERYNEWPALETTE: { tron@4489: HDC hDC = GetWindowDC(hwnd); tron@4489: HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE); tron@4489: UINT nChanged = RealizePalette(hDC); tron@2174: tron@4489: SelectPalette(hDC, hOldPalette, TRUE); tron@4489: ReleaseDC(hwnd, hDC); tron@4489: if (nChanged) InvalidateRect(hwnd, NULL, FALSE); tron@4489: return 0; tron@2174: } tron@2174: tron@4489: case WM_CLOSE: rubidium@4548: HandleExitGameRequest(); tron@2174: return 0; tron@4489: Darkvater@5020: case WM_DESTROY: Darkvater@5020: if (_window_maximize) { Darkvater@5020: _cur_resolution[0] = _bck_resolution[0]; Darkvater@5020: _cur_resolution[1] = _bck_resolution[1]; Darkvater@5020: } Darkvater@5020: return 0; Darkvater@5020: tron@4489: case WM_LBUTTONDOWN: tron@4489: SetCapture(hwnd); tron@4489: _left_button_down = true; Darkvater@5090: HandleMouseEvents(); tron@2174: return 0; tron@4489: tron@4489: case WM_LBUTTONUP: tron@4489: ReleaseCapture(); tron@4489: _left_button_down = false; tron@4489: _left_button_clicked = false; Darkvater@5090: HandleMouseEvents(); tron@4489: return 0; tron@4489: tron@4489: case WM_RBUTTONDOWN: tron@4489: SetCapture(hwnd); tron@4489: _right_button_down = true; tron@4489: _right_button_clicked = true; Darkvater@5090: HandleMouseEvents(); tron@4489: return 0; tron@4489: tron@4489: case WM_RBUTTONUP: tron@4489: ReleaseCapture(); tron@4489: _right_button_down = false; Darkvater@5090: HandleMouseEvents(); tron@4489: return 0; tron@4489: tron@4489: case WM_MOUSELEAVE: tron@4489: UndrawMouseCursor(); tron@4489: _cursor.in_window = false; Darkvater@5021: Darkvater@5021: if (!_left_button_down && !_right_button_down) MyShowCursor(true); Darkvater@5090: HandleMouseEvents(); Darkvater@5021: return 0; tron@4489: tron@4489: case WM_MOUSEMOVE: { tron@4489: int x = (int16)LOWORD(lParam); tron@4489: int y = (int16)HIWORD(lParam); tron@4489: POINT pt; tron@4489: tron@4489: /* If the mouse was not in the window and it has moved it means it has Darkvater@5021: * come into the window, so start drawing the mouse. Also start tron@4489: * tracking the mouse for exiting the window */ tron@4489: if (!_cursor.in_window) { tron@4489: _cursor.in_window = true; tron@4489: SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); tron@4489: Darkvater@5021: DrawMouseCursor(); tron@4489: } tron@4489: tron@4489: if (_wnd.double_size) { tron@4489: x /= 2; tron@4489: y /= 2; tron@4489: } tron@4489: tron@4489: if (_cursor.fix_at) { tron@4489: int dx = x - _cursor.pos.x; tron@4489: int dy = y - _cursor.pos.y; tron@4489: if (dx != 0 || dy != 0) { tron@4489: _cursor.delta.x += dx; tron@4489: _cursor.delta.y += dy; tron@4489: tron@4489: pt.x = _cursor.pos.x; tron@4489: pt.y = _cursor.pos.y; tron@4489: tron@4489: if (_wnd.double_size) { tron@4489: pt.x *= 2; tron@4489: pt.y *= 2; tron@4489: } tron@4489: ClientToScreen(hwnd, &pt); tron@4489: SetCursorPos(pt.x, pt.y); tron@4489: } tron@4489: } else { tron@4489: _cursor.delta.x += x - _cursor.pos.x; tron@4489: _cursor.delta.y += y - _cursor.pos.y; tron@4489: _cursor.pos.x = x; tron@4489: _cursor.pos.y = y; tron@4489: _cursor.dirty = true; tron@4489: } tron@4489: MyShowCursor(false); Darkvater@5090: HandleMouseEvents(); tron@4489: return 0; tron@4489: } tron@4489: tron@4489: case WM_KEYDOWN: { tron@4489: // this is the rewritten ascii input function tron@4489: // it disables windows deadkey handling --> more linux like :D peter1138@5108: wchar_t w = 0; tron@4489: byte ks[256]; tron@4489: uint scancode; Darkvater@5086: uint32 pressed_key; tron@4489: tron@4489: GetKeyboardState(ks); Darkvater@5112: if (ToUnicode(wParam, 0, ks, &w, 1, 0) != 1) { peter1138@5108: /* On win9x ToUnicode always fails, so fall back to ToAscii */ rubidium@5587: if (ToAscii(wParam, 0, ks, (LPWORD)&w, 0) != 1) w = 0; // no translation was possible tron@4489: } tron@4489: Darkvater@5086: pressed_key = w | MapWindowsKey(wParam) << 16; tron@4489: tron@4489: scancode = GB(lParam, 16, 8); Darkvater@5086: if (scancode == 41) pressed_key = w | WKC_BACKQUOTE << 16; tron@4489: Darkvater@5089: if (GB(pressed_key, 16, 16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) { tron@4489: _double_size ^= 1; tron@4489: _wnd.double_size = _double_size; tron@4489: ClientSizeChanged(_wnd.width, _wnd.height); tron@4489: MarkWholeScreenDirty(); tron@4489: } Darkvater@5086: HandleKeypress(pressed_key); tron@2174: break; tron@2174: } tron@2174: tron@4489: case WM_SYSKEYDOWN: /* user presses F10 or Alt, both activating the title-menu */ tron@4489: switch (wParam) { tron@4489: case VK_RETURN: tron@4489: case 'F': /* Full Screen on ALT + ENTER/F */ tron@4489: ToggleFullScreen(!_wnd.fullscreen); tron@4489: return 0; tron@2174: tron@4489: case VK_MENU: /* Just ALT */ tron@4489: return 0; // do nothing tron@4489: tron@4489: case VK_F10: /* F10, ignore activation of menu */ Darkvater@5086: HandleKeypress(MapWindowsKey(wParam) << 16); tron@4489: return 0; tron@4489: tron@4489: default: /* ALT in combination with something else */ Darkvater@5086: HandleKeypress(MapWindowsKey(wParam) << 16); tron@4489: break; tron@4489: } tron@2174: break; tron@4489: tron@4489: case WM_SIZE: tron@4489: if (wParam != SIZE_MINIMIZED) { Darkvater@5019: /* Set maximized flag when we maximize (obviously), but also when we Darkvater@5019: * switched to fullscreen from a maximized state */ Darkvater@5019: _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen)); Darkvater@5020: if (_window_maximize) { Darkvater@5020: _bck_resolution[0] = _cur_resolution[0]; Darkvater@5020: _bck_resolution[1] = _cur_resolution[1]; Darkvater@5020: } tron@4489: ClientSizeChanged(LOWORD(lParam), HIWORD(lParam)); tron@4489: } tron@4489: return 0; tron@4489: tron@4489: case WM_SIZING: { tron@4489: RECT* r = (RECT*)lParam; tron@4489: RECT r2; tron@4489: int w, h; tron@4489: tron@4489: SetRect(&r2, 0, 0, 0, 0); tron@4489: AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); tron@4489: tron@4489: w = r->right - r->left - (r2.right - r2.left); tron@4489: h = r->bottom - r->top - (r2.bottom - r2.top); tron@4489: if (_wnd.double_size) { tron@4489: w /= 2; tron@4489: h /= 2; tron@4489: } tron@4489: w = clamp(w, 64, MAX_SCREEN_WIDTH); tron@4489: h = clamp(h, 64, MAX_SCREEN_HEIGHT); tron@4489: if (_wnd.double_size) { tron@4489: w *= 2; tron@4489: h *= 2; tron@4489: } tron@4489: SetRect(&r2, 0, 0, w, h); tron@4489: tron@4489: AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); tron@4489: w = r2.right - r2.left; tron@4489: h = r2.bottom - r2.top; tron@4489: tron@4489: switch (wParam) { tron@4489: case WMSZ_BOTTOM: tron@4489: r->bottom = r->top + h; tron@4489: break; tron@4489: tron@4489: case WMSZ_BOTTOMLEFT: tron@4489: r->bottom = r->top + h; tron@4489: r->left = r->right - w; tron@4489: break; tron@4489: tron@4489: case WMSZ_BOTTOMRIGHT: tron@4489: r->bottom = r->top + h; tron@4489: r->right = r->left + w; tron@4489: break; tron@4489: tron@4489: case WMSZ_LEFT: tron@4489: r->left = r->right - w; tron@4489: break; tron@4489: tron@4489: case WMSZ_RIGHT: tron@4489: r->right = r->left + w; tron@4489: break; tron@4489: tron@4489: case WMSZ_TOP: tron@4489: r->top = r->bottom - h; tron@4489: break; tron@4489: tron@4489: case WMSZ_TOPLEFT: tron@4489: r->top = r->bottom - h; tron@4489: r->left = r->right - w; tron@4489: break; tron@4489: tron@4489: case WMSZ_TOPRIGHT: tron@4489: r->top = r->bottom - h; tron@4489: r->right = r->left + w; tron@4489: break; tron@4489: } tron@4489: return TRUE; tron@2174: } tron@2174: tron@2174: // needed for wheel tron@2174: #if !defined(WM_MOUSEWHEEL) tron@4489: # 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@4489: case WM_MOUSEWHEEL: { tron@4489: int delta = GET_WHEEL_DELTA_WPARAM(wParam); tron@2174: tron@4489: if (delta < 0) { tron@4489: _cursor.wheel++; tron@4489: } else if (delta > 0) { tron@4489: _cursor.wheel--; tron@4489: } Darkvater@5090: HandleMouseEvents(); tron@4489: return 0; tron@2174: } tron@2174: tron@4489: case WM_ACTIVATEAPP: rubidium@5587: _wnd.has_focus = (wParam != 0); tron@4489: break; tron@2174: } tron@2174: return DefWindowProc(hwnd, msg, wParam, lParam); tron@2174: } tron@2174: tron@2174: static void RegisterWndClass(void) tron@2174: { tron@4489: static bool registered = false; tron@4489: 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, Darkvater@5168: _T("OTTD") tron@2174: }; tron@4489: tron@2174: registered = true; tron@4489: if (!RegisterClass(&wnd)) error("RegisterClass failed"); tron@2174: } tron@2174: } 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: tron@4489: memset(&settings, 0, sizeof(settings)); tron@4489: settings.dmSize = sizeof(settings); tron@4489: settings.dmFields = tron@4489: (_fullscreen_bpp != 0 ? DM_BITSPERPEL : 0) | tron@4489: DM_PELSWIDTH | tron@4489: DM_PELSHEIGHT | tron@4489: (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0); tron@4489: settings.dmBitsPerPel = _fullscreen_bpp; tron@4489: settings.dmPelsWidth = _wnd.width_org; tron@2174: settings.dmPelsHeight = _wnd.height_org; tron@2174: settings.dmDisplayFrequency = _display_hz; tron@4489: 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; Darkvater@5377: DWORD style, showstyle; tron@2174: int x, y, w, h; tron@2174: Darkvater@5377: showstyle = SW_SHOWNORMAL; tron@2174: _wnd.fullscreen = full_screen; tron@2174: if (_wnd.fullscreen) { Darkvater@5377: style = WS_POPUP; tron@2174: SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org); tron@2174: } else { Darkvater@5377: style = WS_OVERLAPPEDWINDOW; Darkvater@5019: /* On window creation, check if we were in maximize mode before */ Darkvater@5377: if (_window_maximize) showstyle = SW_SHOWMAXIMIZED; 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) { Darkvater@4258: ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL); // remove maximize-flag tron@2174: SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); tron@2174: } else { Darkvater@4840: extern const char _openttd_revision[]; Darkvater@5168: TCHAR Windowtitle[50]; tron@2174: Darkvater@5168: _sntprintf(Windowtitle, sizeof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision)); tron@2174: Darkvater@5168: _wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); Darkvater@4258: if (_wnd.main_wnd == NULL) error("CreateWindow failed"); Darkvater@5377: ShowWindow(_wnd.main_wnd, showstyle); 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: rubidium@5587: bi = (BITMAPINFO*)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); rubidium@5587: _wnd.alloced_bits = _wnd.buffer_bits = (Pixel*)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@4489: if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect); tron@2174: tron@2174: dc = GetDC(0); tron@4489: _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.bitmap_bits, NULL, 0); tron@4489: if (_wnd.dib_sect == NULL) error("CreateDIBSection failed"); tron@2174: ReleaseDC(0, dc); tron@2174: tron@4489: if (!_wnd.double_size) _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@4489: { 640, 480 }, tron@4489: { 800, 600 }, tron@4489: { 1024, 768 }, tron@4489: { 1152, 864 }, tron@4489: { 1280, 800 }, tron@4489: { 1280, 960 }, tron@4489: { 1280, 1024 }, tron@4489: { 1400, 1050 }, tron@4489: { 1600, 1200 }, tron@4489: { 1680, 1050 }, tron@4489: { 1920, 1200 } tron@2174: }; tron@2174: tron@2174: static void FindResolutions(void) tron@2174: { tron@4000: uint n = 0; tron@4000: uint i; Darkvater@5169: DEVMODEA dm; tron@2174: Darkvater@5169: /* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95 Darkvater@5169: * Doesn't really matter since we don't pass a string anyways, but still Darkvater@5169: * a letdown */ Darkvater@5169: for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) { tron@2174: if (dm.dmBitsPerPel == 8 && IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) && tron@4000: IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)) { tron@4000: uint j; tron@4000: 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: { Darkvater@3285: DeleteObject(_wnd.gdi_palette); Darkvater@3285: DeleteObject(_wnd.dib_sect); Darkvater@3285: DestroyWindow(_wnd.main_wnd); Darkvater@3285: 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: } 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; rubidium@5581: uint32 cur_ticks = GetTickCount(); rubidium@5581: uint32 next_tick = cur_ticks + 30; tron@2174: tron@2174: _wnd.running = true; tron@2174: tron@2952: for (;;) { rubidium@5581: uint32 prev_cur_ticks = cur_ticks; // to check for wrapping rubidium@5581: tron@2174: while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) { tron@2174: InteractiveRandom(); // randomness tron@2174: DispatchMessage(&mesg); tron@2174: } tron@2228: if (_exit_game) return; tron@2174: tron@2174: #if defined(_DEBUG) Darkvater@5089: if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 && tron@2174: #else Darkvater@5089: /* Speed up using TAB, but disable for ALT+TAB of course */ Darkvater@5089: if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 && tron@2174: #endif Darkvater@5089: !_networking && _game_mode != GM_MENU) { Darkvater@5089: _fast_forward |= 2; tron@4077: } else if (_fast_forward & 2) { tron@2174: _fast_forward = 0; tron@4077: } tron@2174: tron@2174: cur_ticks = GetTickCount(); rubidium@5581: if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) { rubidium@5581: next_tick = cur_ticks + 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@4077: } else { tron@2174: _dirkeys = 0; tron@4077: } tron@2174: tron@2174: GameLoop(); tron@2174: _cursor.delta.x = _cursor.delta.y = 0; tron@2174: tron@4077: if (_force_full_redraw) 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@4489: static void Win32GdiFullScreen(bool full_screen) tron@4489: { tron@4489: MakeWindow(full_screen); tron@4489: } 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: };