tron@2186: /* $Id$ */ tron@2186: tron@2189: #include "../stdafx.h" tron@2189: #include "../openttd.h" rubidium@8619: #include "../gfx_func.h" tron@2189: #include "../variables.h" tron@2207: #include "../win32.h" truelight@7435: #include "../blitter/factory.hpp" rubidium@8617: #include "../network/network.h" tron@2189: #include "win32_v.h" tron@2171: #include Darkvater@5168: #include truelight@0: truelight@0: static struct { truelight@0: HWND main_wnd; truelight@0: HBITMAP dib_sect; truelight@7374: void *buffer_bits; truelight@0: HPALETTE gdi_palette; tron@4489: int width; tron@4489: int height; tron@4489: int width_org; tron@4489: int height_org; truelight@0: bool fullscreen; truelight@0: bool has_focus; truelight@0: bool running; truelight@0: } _wnd; truelight@0: Darkvater@3051: bool _force_full_redraw; Darkvater@4258: bool _window_maximize; Darkvater@3051: uint _display_hz; Darkvater@3051: uint _fullscreen_bpp; Darkvater@5020: static uint16 _bck_resolution[2]; Darkvater@6577: #if !defined(UNICODE) Darkvater@6577: uint _codepage; Darkvater@6577: #endif Darkvater@3051: rubidium@6573: static void MakePalette() truelight@0: { truelight@0: LOGPALETTE *pal; tron@1468: uint i; truelight@0: rubidium@6987: pal = (LOGPALETTE*)alloca(sizeof(LOGPALETTE) + (256 - 1) * sizeof(PALETTEENTRY)); truelight@0: truelight@0: pal->palVersion = 0x300; truelight@0: pal->palNumEntries = 256; truelight@193: tron@1991: for (i = 0; i != 256; i++) { tron@1991: pal->palPalEntry[i].peRed = _cur_palette[i].r; tron@1991: pal->palPalEntry[i].peGreen = _cur_palette[i].g; tron@1991: pal->palPalEntry[i].peBlue = _cur_palette[i].b; truelight@0: pal->palPalEntry[i].peFlags = 0; truelight@0: truelight@0: } truelight@0: _wnd.gdi_palette = CreatePalette(pal); tron@4489: if (_wnd.gdi_palette == NULL) error("CreatePalette failed!\n"); truelight@0: } truelight@0: truelight@0: static void UpdatePalette(HDC dc, uint start, uint count) truelight@0: { truelight@0: RGBQUAD rgb[256]; truelight@0: uint i; truelight@0: tron@1995: for (i = 0; i != count; i++) { tron@1995: rgb[i].rgbRed = _cur_palette[start + i].r; tron@1995: rgb[i].rgbGreen = _cur_palette[start + i].g; tron@1995: rgb[i].rgbBlue = _cur_palette[start + i].b; truelight@0: rgb[i].rgbReserved = 0; truelight@0: } truelight@0: truelight@0: SetDIBColorTable(dc, start, count, rgb); truelight@0: } truelight@0: rubidium@6574: struct VkMapping { truelight@0: byte vk_from; truelight@0: byte vk_count; truelight@0: byte map_to; rubidium@6574: }; truelight@0: tron@1468: #define AS(x, z) {x, 0, z} tron@1468: #define AM(x, y, z, w) {x, y - x, z} truelight@0: truelight@0: static const VkMapping _vk_mapping[] = { truelight@7807: /* Pageup stuff + up/down */ truelight@0: AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN), truelight@7807: /* Map letters & digits */ truelight@0: AM('A','Z','A','Z'), truelight@0: AM('0','9','0','9'), truelight@0: 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), truelight@0: rubidium@4434: AS(VK_SPACE, WKC_SPACE), rubidium@4434: AS(VK_RETURN, WKC_RETURN), rubidium@4434: AS(VK_TAB, WKC_TAB), truelight@0: truelight@7807: /* Function keys */ rubidium@4434: AM(VK_F1, VK_F12, WKC_F1, WKC_F12), truelight@0: truelight@7807: /* Numeric part */ 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), truelight@7806: AS(VK_DECIMAL, WKC_NUM_DECIMAL), truelight@7806: truelight@7807: /* Other non-letter keys */ truelight@7806: AS(0xBF, WKC_SLASH), truelight@7806: AS(0xBA, WKC_SEMICOLON), truelight@7806: AS(0xBB, WKC_EQUALS), truelight@7806: AS(0xDB, WKC_L_BRACKET), truelight@7806: AS(0xDC, WKC_BACKSLASH), truelight@7806: AS(0xDD, WKC_R_BRACKET), truelight@7806: truelight@7806: AS(0xDE, WKC_SINGLEQUOTE), truelight@7806: AS(0xBC, WKC_COMMA), truelight@7806: AS(0xBD, WKC_MINUS), truelight@7806: AS(0xBE, WKC_PERIOD) truelight@0: }; truelight@0: tron@1466: static uint MapWindowsKey(uint sym) truelight@0: { tron@1466: const VkMapping *map; tron@1466: uint key = 0; truelight@0: tron@1466: for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { tron@1466: if ((uint)(sym - map->vk_from) <= map->vk_count) { tron@1466: key = sym - map->vk_from + map->map_to; tron@1466: break; tron@1466: } tron@1466: } darkvater@135: tron@2026: if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT; tron@2026: if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL; tron@2026: if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT; truelight@0: return key; truelight@0: } truelight@0: truelight@0: static bool AllocateDibSection(int w, int h); truelight@0: truelight@0: static void ClientSizeChanged(int w, int h) truelight@0: { truelight@0: // allocate new dib section of the new size truelight@0: if (AllocateDibSection(w, h)) { truelight@0: // mark all palette colors dirty truelight@0: _pal_first_dirty = 0; glx@7888: _pal_count_dirty = 256; truelight@0: GameSizeChanged(); truelight@193: truelight@0: // redraw screen truelight@0: if (_wnd.running) { truelight@0: _screen.dst_ptr = _wnd.buffer_bits; truelight@0: UpdateWindows(); truelight@0: } truelight@0: } truelight@0: } truelight@0: ludde@2125: #ifdef _DEBUG ludde@2125: // Keep this function here.. ludde@2125: // It allows you to redraw the screen from within the MSVC debugger rubidium@6573: int RedrawScreenDebug() ludde@2125: { ludde@2125: HDC dc,dc2; ludde@2125: static int _fooctr; ludde@2125: HBITMAP old_bmp; ludde@2125: HPALETTE old_palette; ludde@2125: ludde@2125: _screen.dst_ptr = _wnd.buffer_bits; ludde@2125: UpdateWindows(); ludde@2125: ludde@2125: dc = GetDC(_wnd.main_wnd); ludde@2125: dc2 = CreateCompatibleDC(dc); ludde@2125: rubidium@5838: old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect); ludde@2125: old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); ludde@2125: BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); ludde@2125: SelectPalette(dc, old_palette, TRUE); ludde@2125: SelectObject(dc2, old_bmp); ludde@2125: DeleteDC(dc2); ludde@2125: ReleaseDC(_wnd.main_wnd, dc); ludde@2125: ludde@2125: return _fooctr++; ludde@2125: } ludde@2125: #endif ludde@2125: 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: belugas@8667: static bool MakeWindow(bool full_screen) glx@7491: { glx@7491: _fullscreen = full_screen; glx@7491: glx@7491: // recreate window? glx@7491: if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) { rubidium@7925: DestroyWindow(_wnd.main_wnd); glx@7491: _wnd.main_wnd = 0; glx@7491: } glx@7491: glx@7491: #if defined(WINCE) glx@7491: /* WinCE is always fullscreen */ glx@7491: #else glx@7491: if (full_screen) { glx@7491: DEVMODE settings; glx@7491: glx@7491: /* Make sure we are always at least the screen-depth of the blitter */ glx@7491: if (_fullscreen_bpp < BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth()) _fullscreen_bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(); glx@7491: glx@7491: memset(&settings, 0, sizeof(settings)); glx@7491: settings.dmSize = sizeof(settings); glx@7491: settings.dmFields = glx@7491: (_fullscreen_bpp != 0 ? DM_BITSPERPEL : 0) | glx@7491: DM_PELSWIDTH | glx@7491: DM_PELSHEIGHT | glx@7491: (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0); glx@7491: settings.dmBitsPerPel = _fullscreen_bpp; glx@7491: settings.dmPelsWidth = _wnd.width_org; glx@7491: settings.dmPelsHeight = _wnd.height_org; glx@7491: settings.dmDisplayFrequency = _display_hz; glx@7491: glx@7491: if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { belugas@8667: MakeWindow(false); // don't care about the result belugas@8667: return false; // the request failed glx@7491: } glx@7491: } else if (_wnd.fullscreen) { glx@7491: // restore display? glx@7491: ChangeDisplaySettings(NULL, 0); glx@7491: } glx@7491: #endif glx@7491: glx@7491: { glx@7491: RECT r; glx@7491: DWORD style, showstyle; glx@7491: int x, y, w, h; glx@7491: glx@7491: showstyle = SW_SHOWNORMAL; glx@7491: _wnd.fullscreen = full_screen; glx@7491: if (_wnd.fullscreen) { glx@7491: style = WS_POPUP; glx@7491: SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org); glx@7491: } else { glx@7491: style = WS_OVERLAPPEDWINDOW; glx@7491: /* On window creation, check if we were in maximize mode before */ glx@7491: if (_window_maximize) showstyle = SW_SHOWMAXIMIZED; glx@7491: SetRect(&r, 0, 0, _wnd.width, _wnd.height); glx@7491: } glx@7491: glx@7491: #if !defined(WINCE) glx@7491: AdjustWindowRect(&r, style, FALSE); glx@7491: #endif glx@7491: w = r.right - r.left; glx@7491: h = r.bottom - r.top; glx@7491: x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; glx@7491: y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; glx@7491: glx@7491: if (_wnd.main_wnd) { glx@7491: ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL); // remove maximize-flag glx@7491: SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); glx@7491: } else { glx@7491: extern const char _openttd_revision[]; glx@7491: TCHAR Windowtitle[50]; glx@7491: glx@7491: _sntprintf(Windowtitle, sizeof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision)); glx@7491: glx@7491: _wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); glx@7491: if (_wnd.main_wnd == NULL) error("CreateWindow failed"); glx@7491: ShowWindow(_wnd.main_wnd, showstyle); glx@7491: } glx@7491: } glx@7491: GameSizeChanged(); // invalidate all windows, force redraw belugas@8667: return true; // the request succedded glx@7491: } glx@7491: truelight@0: static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) truelight@0: { Darkvater@6603: static uint32 keycode = 0; glx@7035: static bool console = false; Darkvater@6603: tron@1468: switch (msg) { tron@4489: case WM_CREATE: tron@4489: SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); tron@4489: break; truelight@193: tron@4489: case WM_PAINT: { tron@4489: PAINTSTRUCT ps; tron@4489: HDC dc,dc2; tron@4489: HBITMAP old_bmp; tron@4489: HPALETTE old_palette; truelight@0: tron@4489: BeginPaint(hwnd, &ps); tron@4489: dc = ps.hdc; tron@4489: dc2 = CreateCompatibleDC(dc); rubidium@5838: old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect); tron@4489: old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); Darkvater@3312: truelight@7456: if (_pal_count_dirty != 0) { truelight@7457: Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter(); truelight@7457: truelight@7456: switch (blitter->UsePaletteAnimation()) { truelight@7456: case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND: glx@7458: UpdatePalette(dc2, _pal_first_dirty, _pal_count_dirty); truelight@7456: break; truelight@7456: truelight@7456: case Blitter::PALETTE_ANIMATION_BLITTER: truelight@7456: blitter->PaletteAnimate(_pal_first_dirty, _pal_count_dirty); truelight@7456: break; truelight@7456: truelight@7456: case Blitter::PALETTE_ANIMATION_NONE: truelight@7456: break; truelight@7456: truelight@7456: default: truelight@7456: NOT_REACHED(); truelight@7456: } truelight@7456: _pal_count_dirty = 0; tron@4489: } truelight@0: 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; truelight@0: } truelight@193: tron@4489: case WM_PALETTECHANGED: tron@4489: if ((HWND)wParam == hwnd) return 0; tron@4489: /* FALLTHROUGH */ truelight@0: 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); dominik@719: tron@4489: SelectPalette(hDC, hOldPalette, TRUE); tron@4489: ReleaseDC(hwnd, hDC); tron@4489: if (nChanged) InvalidateRect(hwnd, NULL, FALSE); tron@4489: return 0; truelight@0: } truelight@0: tron@4489: case WM_CLOSE: rubidium@4548: HandleExitGameRequest(); truelight@0: 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(); darkvater@1548: 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 (_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: 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: Darkvater@6577: #if !defined(UNICODE) Darkvater@6577: case WM_INPUTLANGCHANGE: { Darkvater@6577: TCHAR locale[6]; Darkvater@6577: LCID lcid = GB(lParam, 0, 16); Darkvater@6577: Darkvater@6577: int len = GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, locale, lengthof(locale)); Darkvater@6577: if (len != 0) _codepage = _ttoi(locale); Darkvater@6577: return 1; Darkvater@6577: } Darkvater@6577: #endif /* UNICODE */ Darkvater@6577: glx@7035: case WM_DEADCHAR: glx@7035: console = GB(lParam, 16, 8) == 41; glx@7035: return 0; glx@7035: Darkvater@6603: case WM_CHAR: { Darkvater@6603: /* Silently drop all non-text messages as those were handled by WM_KEYDOWN */ Darkvater@6603: if (wParam < VK_SPACE) return 0; Darkvater@6603: uint scancode = GB(lParam, 16, 8); Darkvater@6603: uint charcode = wParam; tron@4489: glx@7035: /* If the console key is a dead-key, we need to press it twice to get a WM_CHAR message. glx@7035: * But we then get two WM_CHAR messages, so ignore the first one */ glx@7035: if (console && scancode == 41) { glx@7035: console = false; glx@7035: return 0; glx@7035: } glx@7035: Darkvater@6603: #if !defined(UNICODE) Darkvater@6603: wchar_t w; Darkvater@6603: int len = MultiByteToWideChar(_codepage, 0, (char*)&charcode, 1, &w, 1); Darkvater@6603: charcode = len == 1 ? w : 0; Darkvater@6603: #endif /* UNICODE */ tron@4489: Darkvater@6603: /* No matter the keyboard layout, we will map the '~' to the console */ Darkvater@6603: scancode = scancode == 41 ? (int)WKC_BACKQUOTE : keycode; Darkvater@6603: HandleKeypress(GB(charcode, 0, 16) | (scancode << 16)); Darkvater@6603: return 0; Darkvater@6603: } tron@4489: Darkvater@6603: case WM_KEYDOWN: { Darkvater@6603: keycode = MapWindowsKey(wParam); tron@4489: Darkvater@6603: /* Silently drop all text messages as those will be handled by WM_CHAR Darkvater@6603: * WM_KEYDOWN only handles CTRL+ commands and special keys like VK_LEFT, etc. */ Darkvater@6603: if (keycode == 0 || (keycode > WKC_PAUSE && GB(keycode, 13, 4) == 0)) return 0; Darkvater@6603: glx@8037: /* Keys handled in WM_CHAR */ glx@8037: if ((uint)(GB(keycode, 0, 12) - WKC_NUM_DIV) <= WKC_MINUS - WKC_NUM_DIV) return 0; glx@8037: Darkvater@6603: HandleKeypress(0 | (keycode << 16)); Darkvater@6603: return 0; truelight@0: } truelight@0: 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; truelight@0: 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: } truelight@0: break; tron@4489: tron@4489: case WM_SIZE: glx@7622: 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: truelight@6009: #if !defined(WINCE) 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); skidd13@8418: w = Clamp(w, 64, MAX_SCREEN_WIDTH); skidd13@8418: h = Clamp(h, 64, MAX_SCREEN_HEIGHT); 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; truelight@0: } truelight@6010: #endif truelight@0: truelight@0: // needed for wheel truelight@0: #if !defined(WM_MOUSEWHEEL) tron@4489: # define WM_MOUSEWHEEL 0x020A truelight@0: #endif //WM_MOUSEWHEEL truelight@0: #if !defined(GET_WHEEL_DELTA_WPARAM) truelight@0: # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam)) truelight@0: #endif //GET_WHEEL_DELTA_WPARAM truelight@0: tron@4489: case WM_MOUSEWHEEL: { tron@4489: int delta = GET_WHEEL_DELTA_WPARAM(wParam); tron@1468: 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; truelight@0: } truelight@0: glx@7622: case WM_SETFOCUS: glx@7622: _wnd.has_focus = true; glx@7622: break; glx@7622: glx@7622: case WM_KILLFOCUS: glx@7622: _wnd.has_focus = false; glx@7622: break; glx@7622: glx@7429: #if !defined(WINCE) glx@7622: case WM_ACTIVATE: { glx@7926: /* Don't do anything if we are closing openttd */ glx@7926: if (_exit_game) break; glx@7926: glx@7622: bool active = (LOWORD(wParam) != WA_INACTIVE); glx@7622: bool minimized = (HIWORD(wParam) != 0); glx@7429: if (_wnd.fullscreen) { glx@7622: if (active && minimized) { glx@7429: /* Restore the game window */ glx@7429: ShowWindow(hwnd, SW_RESTORE); glx@7491: MakeWindow(true); glx@7622: } else if (!active && !minimized) { glx@7429: /* Minimise the window and restore desktop */ glx@7429: ShowWindow(hwnd, SW_MINIMIZE); glx@7429: ChangeDisplaySettings(NULL, 0); glx@7429: } glx@7429: } truelight@7905: } break; truelight@7905: #endif glx@7622: } glx@7622: truelight@0: return DefWindowProc(hwnd, msg, wParam, lParam); truelight@0: } truelight@0: rubidium@6573: static void RegisterWndClass() truelight@0: { tron@4489: static bool registered = false; tron@4489: truelight@0: if (!registered) { truelight@0: HINSTANCE hinst = GetModuleHandle(NULL); truelight@0: WNDCLASS wnd = { truelight@0: 0, truelight@0: WndProcGdi, truelight@0: 0, truelight@0: 0, truelight@0: hinst, truelight@0: LoadIcon(hinst, MAKEINTRESOURCE(100)), truelight@0: LoadCursor(NULL, IDC_ARROW), truelight@0: 0, truelight@0: 0, Darkvater@5168: _T("OTTD") truelight@0: }; tron@4489: truelight@0: registered = true; tron@4489: if (!RegisterClass(&wnd)) error("RegisterClass failed"); truelight@0: } truelight@0: } truelight@0: truelight@0: static bool AllocateDibSection(int w, int h) truelight@0: { truelight@0: BITMAPINFO *bi; truelight@0: HDC dc; truelight@7374: int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(); truelight@0: skidd13@8418: w = Clamp(w, 64, MAX_SCREEN_WIDTH); skidd13@8418: h = Clamp(h, 64, MAX_SCREEN_HEIGHT); truelight@0: truelight@7374: if (bpp == 0) error("Can't use a blitter that blits 0 bpp for normal visuals"); truelight@7374: truelight@0: if (w == _screen.width && h == _screen.height) truelight@0: return false; truelight@0: darkvater@306: _screen.width = w; skidd13@8423: _screen.pitch = (bpp == 8) ? Align(w, 4) : w; truelight@0: _screen.height = h; rubidium@6987: bi = (BITMAPINFO*)alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256); rubidium@6987: memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256); truelight@0: bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); truelight@193: darkvater@306: bi->bmiHeader.biWidth = _wnd.width = w; darkvater@306: bi->bmiHeader.biHeight = -(_wnd.height = h); truelight@193: truelight@0: bi->bmiHeader.biPlanes = 1; truelight@7374: bi->bmiHeader.biBitCount = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(); truelight@0: bi->bmiHeader.biCompression = BI_RGB; truelight@0: tron@4489: if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect); truelight@0: truelight@0: dc = GetDC(0); truelight@7374: _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.buffer_bits, NULL, 0); tron@4489: if (_wnd.dib_sect == NULL) error("CreateDIBSection failed"); truelight@0: ReleaseDC(0, dc); truelight@0: truelight@0: return true; truelight@0: } truelight@0: truelight@0: 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 } truelight@0: }; truelight@0: rubidium@6573: static void FindResolutions() truelight@0: { tron@4000: uint n = 0; truelight@6009: #if defined(WINCE) truelight@6009: /* EnumDisplaySettingsW is only supported in CE 4.2+ */ truelight@6009: /* XXX -- One might argue that we assume 4.2+ on every system. Then we can use this function safely */ truelight@6009: #else tron@4000: uint i; Darkvater@5169: DEVMODEA dm; truelight@0: 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++) { skidd13@8450: if (dm.dmBitsPerPel == BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() && IsInsideMM(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) && skidd13@8450: IsInsideMM(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)) { tron@4000: uint j; tron@4000: Darkvater@1806: for (j = 0; j < n; j++) { Darkvater@1806: if (_resolutions[j][0] == dm.dmPelsWidth && _resolutions[j][1] == dm.dmPelsHeight) break; Darkvater@1806: } Darkvater@1806: Darkvater@1806: /* In the previous loop we have checked already existing/added resolutions if Darkvater@1806: * they are the same as the new ones. If this is not the case (j == n); we have Darkvater@1806: * looped all and found none, add the new one to the list. If we have reached the Darkvater@1806: * maximum amount of resolutions, then quit querying the display */ Darkvater@1806: if (j == n) { Darkvater@1806: _resolutions[j][0] = dm.dmPelsWidth; Darkvater@1806: _resolutions[j][1] = dm.dmPelsHeight; Darkvater@1806: if (++n == lengthof(_resolutions)) break; Darkvater@1806: } truelight@0: } truelight@0: } truelight@6009: #endif truelight@0: Darkvater@1806: /* We have found no resolutions, show the default list */ truelight@0: if (n == 0) { truelight@0: memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); tron@1468: n = lengthof(default_resolutions); truelight@0: } truelight@0: truelight@0: _num_resolutions = n; Darkvater@1806: SortResolutions(_num_resolutions); truelight@0: } truelight@0: peter1138@7666: static FVideoDriver_Win32 iFVideoDriver_Win32; truelight@0: peter1138@7666: const char *VideoDriver_Win32::Start(const char * const *parm) truelight@0: { truelight@0: memset(&_wnd, 0, sizeof(_wnd)); truelight@0: truelight@0: RegisterWndClass(); truelight@0: truelight@0: MakePalette(); truelight@0: truelight@0: FindResolutions(); truelight@0: truelight@7904: DEBUG(driver, 2, "Resolution for display: %dx%d", _cur_resolution[0], _cur_resolution[1]); truelight@7904: truelight@0: // fullscreen uses those truelight@0: _wnd.width_org = _cur_resolution[0]; truelight@0: _wnd.height_org = _cur_resolution[1]; truelight@0: truelight@0: AllocateDibSection(_cur_resolution[0], _cur_resolution[1]); truelight@0: MarkWholeScreenDirty(); truelight@0: truelight@0: MakeWindow(_fullscreen); truelight@0: truelight@0: return NULL; truelight@0: } truelight@0: peter1138@7666: void VideoDriver_Win32::Stop() truelight@0: { Darkvater@3285: DeleteObject(_wnd.gdi_palette); Darkvater@3285: DeleteObject(_wnd.dib_sect); rubidium@7925: DestroyWindow(_wnd.main_wnd); Darkvater@3285: truelight@6009: #if !defined(WINCE) tron@1468: if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0); truelight@6009: #endif truelight@0: MyShowCursor(true); truelight@0: } truelight@0: peter1138@7666: void VideoDriver_Win32::MakeDirty(int left, int top, int width, int height) truelight@0: { tron@1468: RECT r = { left, top, left + width, top + height }; tron@1468: truelight@0: InvalidateRect(_wnd.main_wnd, &r, FALSE); truelight@0: } truelight@0: rubidium@6573: static void CheckPaletteAnim() truelight@0: { glx@7458: if (_pal_count_dirty == 0) truelight@0: return; truelight@0: InvalidateRect(_wnd.main_wnd, NULL, FALSE); truelight@0: } truelight@0: peter1138@7666: void VideoDriver_Win32::MainLoop() truelight@0: { truelight@0: MSG mesg; rubidium@5832: uint32 cur_ticks = GetTickCount(); truelight@7514: uint32 last_cur_ticks = cur_ticks; rubidium@5832: uint32 next_tick = cur_ticks + 30; truelight@0: truelight@0: _wnd.running = true; truelight@0: tron@2952: for (;;) { rubidium@5832: uint32 prev_cur_ticks = cur_ticks; // to check for wrapping rubidium@5832: truelight@0: while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) { truelight@0: InteractiveRandom(); // randomness Darkvater@6603: TranslateMessage(&mesg); truelight@0: DispatchMessage(&mesg); truelight@0: } tron@2228: if (_exit_game) return; truelight@0: truelight@0: #if defined(_DEBUG) Darkvater@5089: if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 && truelight@0: #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 && darkvater@1548: #endif Darkvater@5089: !_networking && _game_mode != GM_MENU) { Darkvater@5089: _fast_forward |= 2; tron@4077: } else if (_fast_forward & 2) { truelight@0: _fast_forward = 0; tron@4077: } truelight@193: darkvater@1403: cur_ticks = GetTickCount(); truelight@6557: if (cur_ticks >= next_tick || (_fast_forward && !_pause_game) || cur_ticks < prev_cur_ticks) { truelight@7515: _realtime_tick += cur_ticks - last_cur_ticks; truelight@7515: last_cur_ticks = cur_ticks; rubidium@5832: next_tick = cur_ticks + 30; truelight@0: _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; truelight@0: _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; truelight@193: truelight@0: // determine which directional keys are down darkvater@1230: if (_wnd.has_focus) { darkvater@1230: _dirkeys = darkvater@1230: (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) + darkvater@1230: (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) + darkvater@1230: (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) + darkvater@1230: (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0); tron@4077: } else { darkvater@1230: _dirkeys = 0; tron@4077: } truelight@0: truelight@193: GameLoop(); truelight@0: _cursor.delta.x = _cursor.delta.y = 0; truelight@0: tron@4077: if (_force_full_redraw) MarkWholeScreenDirty(); truelight@0: truelight@6009: #if !defined(WINCE) truelight@0: GdiFlush(); truelight@6009: #endif truelight@0: _screen.dst_ptr = _wnd.buffer_bits; truelight@0: UpdateWindows(); truelight@0: CheckPaletteAnim(); truelight@0: } else { truelight@0: Sleep(1); truelight@6009: #if !defined(WINCE) truelight@0: GdiFlush(); truelight@6009: #endif truelight@0: _screen.dst_ptr = _wnd.buffer_bits; rubidium@7950: DrawChatMessage(); truelight@0: DrawMouseCursor(); truelight@0: } truelight@0: } truelight@0: } truelight@0: peter1138@7666: bool VideoDriver_Win32::ChangeResolution(int w, int h) truelight@0: { truelight@0: _wnd.width = _wnd.width_org = w; truelight@0: _wnd.height = _wnd.height_org = h; truelight@193: belugas@8667: return MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching truelight@0: } truelight@0: belugas@8667: bool VideoDriver_Win32::ToggleFullscreen(bool full_screen) tron@4489: { belugas@8667: return MakeWindow(full_screen); tron@4489: }