From 07c30785ab226c4762b442f5a09ec745017cc94b Mon Sep 17 00:00:00 2001 From: KUDr Date: Wed, 10 Jan 2007 18:12:09 +0000 Subject: (svn r8033) [cpp] - Prepare for merge from branches/cpp (all .c files renamed to .cpp) --- src/video/dedicated_v.c | 296 ---------------- src/video/dedicated_v.cpp | 296 ++++++++++++++++ src/video/null_v.c | 45 --- src/video/null_v.cpp | 45 +++ src/video/sdl_v.c | 513 --------------------------- src/video/sdl_v.cpp | 513 +++++++++++++++++++++++++++ src/video/win32_v.c | 876 ---------------------------------------------- src/video/win32_v.cpp | 876 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 1730 insertions(+), 1730 deletions(-) delete mode 100644 src/video/dedicated_v.c create mode 100644 src/video/dedicated_v.cpp delete mode 100644 src/video/null_v.c create mode 100644 src/video/null_v.cpp delete mode 100644 src/video/sdl_v.c create mode 100644 src/video/sdl_v.cpp delete mode 100644 src/video/win32_v.c create mode 100644 src/video/win32_v.cpp (limited to 'src/video') diff --git a/src/video/dedicated_v.c b/src/video/dedicated_v.c deleted file mode 100644 index 764df3afd..000000000 --- a/src/video/dedicated_v.c +++ /dev/null @@ -1,296 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" - -#ifdef ENABLE_NETWORK - -#include "../openttd.h" -#include "../debug.h" -#include "../functions.h" -#include "../gfx.h" -#include "../network/network.h" -#include "../window.h" -#include "../console.h" -#include "../variables.h" -#include "../genworld.h" -#include "dedicated_v.h" - -#ifdef BEOS_NET_SERVER -#include -#endif - -#ifdef __OS2__ -# include /* gettimeofday */ -# include -# include -# include - -# define INCL_DOS -# include - -# define STDIN 0 /* file descriptor for standard input */ - -/** - * Switches OpenTTD to a console app at run-time, instead of a PM app - * Necessary to see stdout, etc. */ -static void OS2_SwitchToConsoleMode(void) -{ - PPIB pib; - PTIB tib; - - DosGetInfoBlocks(&tib, &pib); - - // Change flag from PM to VIO - pib->pib_ultype = 3; -} -#endif - -#ifdef UNIX -# include /* gettimeofday */ -# include -# include -# include -# define STDIN 0 /* file descriptor for standard input */ - -/* Signal handlers */ -static void DedicatedSignalHandler(int sig) -{ - _exit_game = true; - signal(sig, DedicatedSignalHandler); -} -#endif - -#ifdef WIN32 -#include /* GetTickCount */ -#include -#include -#include -static HANDLE _hInputReady, _hWaitForInputHandling; -static HANDLE _hThread; // Thread to close -static char _win_console_thread_buffer[200]; - -/* Windows Console thread. Just loop and signal when input has been received */ -static void WINAPI CheckForConsoleInput(void) -{ - while (true) { - fgets(_win_console_thread_buffer, lengthof(_win_console_thread_buffer), stdin); - /* Signal input waiting that input is read and wait for it being handled - * SignalObjectAndWait() should be used here, but it's unsupported in Win98< */ - SetEvent(_hInputReady); - WaitForSingleObject(_hWaitForInputHandling, INFINITE); - } -} - -static void CreateWindowsConsoleThread(void) -{ - DWORD dwThreadId; - /* Create event to signal when console input is ready */ - _hInputReady = CreateEvent(NULL, false, false, NULL); - _hWaitForInputHandling = CreateEvent(NULL, false, false, NULL); - if (_hInputReady == NULL || _hWaitForInputHandling == NULL) error("Cannot create console event!"); - - _hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CheckForConsoleInput, NULL, 0, &dwThreadId); - if (_hThread == NULL) error("Cannot create console thread!"); - - DEBUG(driver, 2, "Windows console thread started"); -} - -static void CloseWindowsConsoleThread(void) -{ - CloseHandle(_hThread); - CloseHandle(_hInputReady); - CloseHandle(_hWaitForInputHandling); - DEBUG(driver, 2, "Windows console thread shut down"); -} - -#endif - - -static void *_dedicated_video_mem; - -extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm); -extern void SwitchMode(int new_mode); - - -static const char *DedicatedVideoStart(const char * const *parm) -{ - _screen.width = _screen.pitch = _cur_resolution[0]; - _screen.height = _cur_resolution[1]; - _dedicated_video_mem = malloc(_cur_resolution[0]*_cur_resolution[1]); - - SetDebugString("net=6"); - -#ifdef WIN32 - // For win32 we need to allocate a console (debug mode does the same) - CreateConsole(); - CreateWindowsConsoleThread(); - SetConsoleTitle(_T("OpenTTD Dedicated Server")); -#endif - -#ifdef __OS2__ - // For OS/2 we also need to switch to console mode instead of PM mode - OS2_SwitchToConsoleMode(); -#endif - - DEBUG(driver, 1, "Loading dedicated server"); - return NULL; -} - -static void DedicatedVideoStop(void) -{ -#ifdef WIN32 - CloseWindowsConsoleThread(); -#endif - free(_dedicated_video_mem); -} - -static void DedicatedVideoMakeDirty(int left, int top, int width, int height) {} -static bool DedicatedVideoChangeRes(int w, int h) { return false; } -static void DedicatedVideoFullScreen(bool fs) {} - -#if defined(UNIX) || defined(__OS2__) -static bool InputWaiting(void) -{ - struct timeval tv; - fd_set readfds; - - tv.tv_sec = 0; - tv.tv_usec = 1; - - FD_ZERO(&readfds); - FD_SET(STDIN, &readfds); - - /* don't care about writefds and exceptfds: */ - return select(STDIN + 1, &readfds, NULL, NULL, &tv) > 0; -} - -static uint32 GetTime(void) -{ - struct timeval tim; - - gettimeofday(&tim, NULL); - return tim.tv_usec / 1000 + tim.tv_sec * 1000; -} - -#else - -static bool InputWaiting(void) -{ - return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0; -} - -static uint32 GetTime(void) -{ - return GetTickCount(); -} - -#endif - -static void DedicatedHandleKeyInput(void) -{ - static char input_line[200] = ""; - - if (!InputWaiting()) return; - - if (_exit_game) return; - -#if defined(UNIX) || defined(__OS2__) - if (fgets(input_line, lengthof(input_line), stdin) == NULL) return; -#else - /* Handle console input, and singal console thread, it can accept input again */ - strncpy(input_line, _win_console_thread_buffer, lengthof(input_line)); - SetEvent(_hWaitForInputHandling); -#endif - - /* XXX - strtok() does not 'forget' \n\r if it is the first character! */ - strtok(input_line, "\r\n"); // Forget about the final \n (or \r) - { /* Remove any special control characters */ - uint i; - for (i = 0; i < lengthof(input_line); i++) { - if (input_line[i] == '\n' || input_line[i] == '\r') // cut missed beginning '\0' - input_line[i] = '\0'; - - if (input_line[i] == '\0') - break; - - if (!IS_INT_INSIDE(input_line[i], ' ', 256)) - input_line[i] = ' '; - } - } - - IConsoleCmdExec(input_line); // execute command -} - -static void DedicatedVideoMainLoop(void) -{ - uint32 cur_ticks = GetTime(); - uint32 next_tick = cur_ticks + 30; - - /* Signal handlers */ -#ifdef UNIX - signal(SIGTERM, DedicatedSignalHandler); - signal(SIGINT, DedicatedSignalHandler); - signal(SIGQUIT, DedicatedSignalHandler); -#endif - - // Load the dedicated server stuff - _is_network_server = true; - _network_dedicated = true; - _network_playas = PLAYER_SPECTATOR; - _local_player = PLAYER_SPECTATOR; - - /* If SwitchMode is SM_LOAD, it means that the user used the '-g' options */ - if (_switch_mode != SM_LOAD) { - StartNewGameWithoutGUI(GENERATE_NEW_SEED); - SwitchMode(_switch_mode); - _switch_mode = SM_NONE; - } else { - _switch_mode = SM_NONE; - /* First we need to test if the savegame can be loaded, else we will end up playing the - * intro game... */ - if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) { - /* Loading failed, pop out.. */ - DEBUG(net, 0, "Loading requested map failed, aborting"); - _networking = false; - } else { - /* We can load this game, so go ahead */ - SwitchMode(SM_LOAD); - } - } - - // Done loading, start game! - - if (!_networking) { - DEBUG(net, 0, "Dedicated server could not be started, aborting"); - return; - } - - while (!_exit_game) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping - InteractiveRandom(); // randomness - - if (!_dedicated_forks) - DedicatedHandleKeyInput(); - - cur_ticks = GetTime(); - if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks) { - next_tick = cur_ticks + 30; - - GameLoop(); - _screen.dst_ptr = _dedicated_video_mem; - UpdateWindows(); - } - CSleep(1); - } -} - -const HalVideoDriver _dedicated_video_driver = { - DedicatedVideoStart, - DedicatedVideoStop, - DedicatedVideoMakeDirty, - DedicatedVideoMainLoop, - DedicatedVideoChangeRes, - DedicatedVideoFullScreen, -}; - -#endif /* ENABLE_NETWORK */ diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp new file mode 100644 index 000000000..764df3afd --- /dev/null +++ b/src/video/dedicated_v.cpp @@ -0,0 +1,296 @@ +/* $Id$ */ + +#include "../stdafx.h" + +#ifdef ENABLE_NETWORK + +#include "../openttd.h" +#include "../debug.h" +#include "../functions.h" +#include "../gfx.h" +#include "../network/network.h" +#include "../window.h" +#include "../console.h" +#include "../variables.h" +#include "../genworld.h" +#include "dedicated_v.h" + +#ifdef BEOS_NET_SERVER +#include +#endif + +#ifdef __OS2__ +# include /* gettimeofday */ +# include +# include +# include + +# define INCL_DOS +# include + +# define STDIN 0 /* file descriptor for standard input */ + +/** + * Switches OpenTTD to a console app at run-time, instead of a PM app + * Necessary to see stdout, etc. */ +static void OS2_SwitchToConsoleMode(void) +{ + PPIB pib; + PTIB tib; + + DosGetInfoBlocks(&tib, &pib); + + // Change flag from PM to VIO + pib->pib_ultype = 3; +} +#endif + +#ifdef UNIX +# include /* gettimeofday */ +# include +# include +# include +# define STDIN 0 /* file descriptor for standard input */ + +/* Signal handlers */ +static void DedicatedSignalHandler(int sig) +{ + _exit_game = true; + signal(sig, DedicatedSignalHandler); +} +#endif + +#ifdef WIN32 +#include /* GetTickCount */ +#include +#include +#include +static HANDLE _hInputReady, _hWaitForInputHandling; +static HANDLE _hThread; // Thread to close +static char _win_console_thread_buffer[200]; + +/* Windows Console thread. Just loop and signal when input has been received */ +static void WINAPI CheckForConsoleInput(void) +{ + while (true) { + fgets(_win_console_thread_buffer, lengthof(_win_console_thread_buffer), stdin); + /* Signal input waiting that input is read and wait for it being handled + * SignalObjectAndWait() should be used here, but it's unsupported in Win98< */ + SetEvent(_hInputReady); + WaitForSingleObject(_hWaitForInputHandling, INFINITE); + } +} + +static void CreateWindowsConsoleThread(void) +{ + DWORD dwThreadId; + /* Create event to signal when console input is ready */ + _hInputReady = CreateEvent(NULL, false, false, NULL); + _hWaitForInputHandling = CreateEvent(NULL, false, false, NULL); + if (_hInputReady == NULL || _hWaitForInputHandling == NULL) error("Cannot create console event!"); + + _hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CheckForConsoleInput, NULL, 0, &dwThreadId); + if (_hThread == NULL) error("Cannot create console thread!"); + + DEBUG(driver, 2, "Windows console thread started"); +} + +static void CloseWindowsConsoleThread(void) +{ + CloseHandle(_hThread); + CloseHandle(_hInputReady); + CloseHandle(_hWaitForInputHandling); + DEBUG(driver, 2, "Windows console thread shut down"); +} + +#endif + + +static void *_dedicated_video_mem; + +extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm); +extern void SwitchMode(int new_mode); + + +static const char *DedicatedVideoStart(const char * const *parm) +{ + _screen.width = _screen.pitch = _cur_resolution[0]; + _screen.height = _cur_resolution[1]; + _dedicated_video_mem = malloc(_cur_resolution[0]*_cur_resolution[1]); + + SetDebugString("net=6"); + +#ifdef WIN32 + // For win32 we need to allocate a console (debug mode does the same) + CreateConsole(); + CreateWindowsConsoleThread(); + SetConsoleTitle(_T("OpenTTD Dedicated Server")); +#endif + +#ifdef __OS2__ + // For OS/2 we also need to switch to console mode instead of PM mode + OS2_SwitchToConsoleMode(); +#endif + + DEBUG(driver, 1, "Loading dedicated server"); + return NULL; +} + +static void DedicatedVideoStop(void) +{ +#ifdef WIN32 + CloseWindowsConsoleThread(); +#endif + free(_dedicated_video_mem); +} + +static void DedicatedVideoMakeDirty(int left, int top, int width, int height) {} +static bool DedicatedVideoChangeRes(int w, int h) { return false; } +static void DedicatedVideoFullScreen(bool fs) {} + +#if defined(UNIX) || defined(__OS2__) +static bool InputWaiting(void) +{ + struct timeval tv; + fd_set readfds; + + tv.tv_sec = 0; + tv.tv_usec = 1; + + FD_ZERO(&readfds); + FD_SET(STDIN, &readfds); + + /* don't care about writefds and exceptfds: */ + return select(STDIN + 1, &readfds, NULL, NULL, &tv) > 0; +} + +static uint32 GetTime(void) +{ + struct timeval tim; + + gettimeofday(&tim, NULL); + return tim.tv_usec / 1000 + tim.tv_sec * 1000; +} + +#else + +static bool InputWaiting(void) +{ + return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0; +} + +static uint32 GetTime(void) +{ + return GetTickCount(); +} + +#endif + +static void DedicatedHandleKeyInput(void) +{ + static char input_line[200] = ""; + + if (!InputWaiting()) return; + + if (_exit_game) return; + +#if defined(UNIX) || defined(__OS2__) + if (fgets(input_line, lengthof(input_line), stdin) == NULL) return; +#else + /* Handle console input, and singal console thread, it can accept input again */ + strncpy(input_line, _win_console_thread_buffer, lengthof(input_line)); + SetEvent(_hWaitForInputHandling); +#endif + + /* XXX - strtok() does not 'forget' \n\r if it is the first character! */ + strtok(input_line, "\r\n"); // Forget about the final \n (or \r) + { /* Remove any special control characters */ + uint i; + for (i = 0; i < lengthof(input_line); i++) { + if (input_line[i] == '\n' || input_line[i] == '\r') // cut missed beginning '\0' + input_line[i] = '\0'; + + if (input_line[i] == '\0') + break; + + if (!IS_INT_INSIDE(input_line[i], ' ', 256)) + input_line[i] = ' '; + } + } + + IConsoleCmdExec(input_line); // execute command +} + +static void DedicatedVideoMainLoop(void) +{ + uint32 cur_ticks = GetTime(); + uint32 next_tick = cur_ticks + 30; + + /* Signal handlers */ +#ifdef UNIX + signal(SIGTERM, DedicatedSignalHandler); + signal(SIGINT, DedicatedSignalHandler); + signal(SIGQUIT, DedicatedSignalHandler); +#endif + + // Load the dedicated server stuff + _is_network_server = true; + _network_dedicated = true; + _network_playas = PLAYER_SPECTATOR; + _local_player = PLAYER_SPECTATOR; + + /* If SwitchMode is SM_LOAD, it means that the user used the '-g' options */ + if (_switch_mode != SM_LOAD) { + StartNewGameWithoutGUI(GENERATE_NEW_SEED); + SwitchMode(_switch_mode); + _switch_mode = SM_NONE; + } else { + _switch_mode = SM_NONE; + /* First we need to test if the savegame can be loaded, else we will end up playing the + * intro game... */ + if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) { + /* Loading failed, pop out.. */ + DEBUG(net, 0, "Loading requested map failed, aborting"); + _networking = false; + } else { + /* We can load this game, so go ahead */ + SwitchMode(SM_LOAD); + } + } + + // Done loading, start game! + + if (!_networking) { + DEBUG(net, 0, "Dedicated server could not be started, aborting"); + return; + } + + while (!_exit_game) { + uint32 prev_cur_ticks = cur_ticks; // to check for wrapping + InteractiveRandom(); // randomness + + if (!_dedicated_forks) + DedicatedHandleKeyInput(); + + cur_ticks = GetTime(); + if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks) { + next_tick = cur_ticks + 30; + + GameLoop(); + _screen.dst_ptr = _dedicated_video_mem; + UpdateWindows(); + } + CSleep(1); + } +} + +const HalVideoDriver _dedicated_video_driver = { + DedicatedVideoStart, + DedicatedVideoStop, + DedicatedVideoMakeDirty, + DedicatedVideoMainLoop, + DedicatedVideoChangeRes, + DedicatedVideoFullScreen, +}; + +#endif /* ENABLE_NETWORK */ diff --git a/src/video/null_v.c b/src/video/null_v.c deleted file mode 100644 index ecdd3486c..000000000 --- a/src/video/null_v.c +++ /dev/null @@ -1,45 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" -#include "../openttd.h" -#include "../gfx.h" -#include "../variables.h" -#include "../window.h" -#include "null_v.h" - -static void* _null_video_mem = NULL; - -static const char* NullVideoStart(const char* const* parm) -{ - _screen.width = _screen.pitch = _cur_resolution[0]; - _screen.height = _cur_resolution[1]; - _null_video_mem = malloc(_cur_resolution[0] * _cur_resolution[1]); - return NULL; -} - -static void NullVideoStop(void) { free(_null_video_mem); } - -static void NullVideoMakeDirty(int left, int top, int width, int height) {} - -static void NullVideoMainLoop(void) -{ - uint i; - - for (i = 0; i < 1000; i++) { - GameLoop(); - _screen.dst_ptr = _null_video_mem; - UpdateWindows(); - } -} - -static bool NullVideoChangeRes(int w, int h) { return false; } -static void NullVideoFullScreen(bool fs) {} - -const HalVideoDriver _null_video_driver = { - NullVideoStart, - NullVideoStop, - NullVideoMakeDirty, - NullVideoMainLoop, - NullVideoChangeRes, - NullVideoFullScreen, -}; diff --git a/src/video/null_v.cpp b/src/video/null_v.cpp new file mode 100644 index 000000000..ecdd3486c --- /dev/null +++ b/src/video/null_v.cpp @@ -0,0 +1,45 @@ +/* $Id$ */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../gfx.h" +#include "../variables.h" +#include "../window.h" +#include "null_v.h" + +static void* _null_video_mem = NULL; + +static const char* NullVideoStart(const char* const* parm) +{ + _screen.width = _screen.pitch = _cur_resolution[0]; + _screen.height = _cur_resolution[1]; + _null_video_mem = malloc(_cur_resolution[0] * _cur_resolution[1]); + return NULL; +} + +static void NullVideoStop(void) { free(_null_video_mem); } + +static void NullVideoMakeDirty(int left, int top, int width, int height) {} + +static void NullVideoMainLoop(void) +{ + uint i; + + for (i = 0; i < 1000; i++) { + GameLoop(); + _screen.dst_ptr = _null_video_mem; + UpdateWindows(); + } +} + +static bool NullVideoChangeRes(int w, int h) { return false; } +static void NullVideoFullScreen(bool fs) {} + +const HalVideoDriver _null_video_driver = { + NullVideoStart, + NullVideoStop, + NullVideoMakeDirty, + NullVideoMainLoop, + NullVideoChangeRes, + NullVideoFullScreen, +}; diff --git a/src/video/sdl_v.c b/src/video/sdl_v.c deleted file mode 100644 index 3e776d29f..000000000 --- a/src/video/sdl_v.c +++ /dev/null @@ -1,513 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" - -#ifdef WITH_SDL - -#include "../openttd.h" -#include "../debug.h" -#include "../functions.h" -#include "../gfx.h" -#include "../macros.h" -#include "../sdl.h" -#include "../window.h" -#include "../network/network.h" -#include "../variables.h" -#include "sdl_v.h" -#include - -static SDL_Surface *_sdl_screen; -static bool _all_modes; - -#define MAX_DIRTY_RECTS 100 -static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS]; -static int _num_dirty_rects; - -static void SdlVideoMakeDirty(int left, int top, int width, int height) -{ - if (_num_dirty_rects < MAX_DIRTY_RECTS) { - _dirty_rects[_num_dirty_rects].x = left; - _dirty_rects[_num_dirty_rects].y = top; - _dirty_rects[_num_dirty_rects].w = width; - _dirty_rects[_num_dirty_rects].h = height; - } - _num_dirty_rects++; -} - -static void UpdatePalette(uint start, uint count) -{ - SDL_Color pal[256]; - uint i; - - for (i = 0; i != count; i++) { - pal[i].r = _cur_palette[start + i].r; - pal[i].g = _cur_palette[start + i].g; - pal[i].b = _cur_palette[start + i].b; - pal[i].unused = 0; - } - - SDL_CALL SDL_SetColors(_sdl_screen, pal, start, count); -} - -static void InitPalette(void) -{ - UpdatePalette(0, 256); -} - -static void CheckPaletteAnim(void) -{ - if (_pal_last_dirty != -1) { - UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); - _pal_last_dirty = -1; - } -} - -static void DrawSurfaceToScreen(void) -{ - int n = _num_dirty_rects; - if (n != 0) { - _num_dirty_rects = 0; - if (n > MAX_DIRTY_RECTS) - SDL_CALL SDL_UpdateRect(_sdl_screen, 0, 0, 0, 0); - else - SDL_CALL SDL_UpdateRects(_sdl_screen, n, _dirty_rects); - } -} - -static const uint16 default_resolutions[][2] = { - { 640, 480}, - { 800, 600}, - {1024, 768}, - {1152, 864}, - {1280, 800}, - {1280, 960}, - {1280, 1024}, - {1400, 1050}, - {1600, 1200}, - {1680, 1050}, - {1920, 1200} -}; - -static void GetVideoModes(void) -{ - int i; - SDL_Rect **modes; - - modes = SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE + (_fullscreen ? SDL_FULLSCREEN : 0)); - - if (modes == NULL) - error("sdl: no modes available"); - - _all_modes = (modes == (void*)-1); - - if (_all_modes) { - // all modes available, put some default ones here - memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); - _num_resolutions = lengthof(default_resolutions); - } else { - int n = 0; - for (i = 0; modes[i]; i++) { - int w = modes[i]->w; - int h = modes[i]->h; - if (IS_INT_INSIDE(w, 640, MAX_SCREEN_WIDTH + 1) && - IS_INT_INSIDE(h, 480, MAX_SCREEN_HEIGHT + 1)) { - int j; - for (j = 0; j < n; j++) { - if (_resolutions[j][0] == w && _resolutions[j][1] == h) break; - } - - if (j == n) { - _resolutions[j][0] = w; - _resolutions[j][1] = h; - if (++n == lengthof(_resolutions)) break; - } - } - } - _num_resolutions = n; - SortResolutions(_num_resolutions); - } -} - -static void GetAvailableVideoMode(int *w, int *h) -{ - int i; - int best; - uint delta; - - // all modes available? - if (_all_modes) return; - - // is the wanted mode among the available modes? - for (i = 0; i != _num_resolutions; i++) { - if (*w == _resolutions[i][0] && *h == _resolutions[i][1]) return; - } - - // use the closest possible resolution - best = 0; - delta = abs((_resolutions[0][0] - *w) * (_resolutions[0][1] - *h)); - for (i = 1; i != _num_resolutions; ++i) { - uint newdelta = abs((_resolutions[i][0] - *w) * (_resolutions[i][1] - *h)); - if (newdelta < delta) { - best = i; - delta = newdelta; - } - } - *w = _resolutions[best][0]; - *h = _resolutions[best][1]; -} - -#ifndef ICON_DIR -#define ICON_DIR "media" -#endif - -#ifdef WIN32 -/* Let's redefine the LoadBMP macro with because we are dynamically - * loading SDL and need to 'SDL_CALL' all functions */ -#undef SDL_LoadBMP -#define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_CALL SDL_RWFromFile(file, "rb"), 1) -#endif - -static bool CreateMainSurface(int w, int h) -{ - extern const char _openttd_revision[]; - SDL_Surface *newscreen, *icon; - char caption[50]; - - GetAvailableVideoMode(&w, &h); - - DEBUG(driver, 1, "SDL: using mode %dx%d", w, h); - - /* Give the application an icon */ - icon = SDL_CALL SDL_LoadBMP(ICON_DIR PATHSEP "openttd.32.bmp"); - if (icon != NULL) { - /* Get the colourkey, which will be magenta */ - uint32 rgbmap = SDL_CALL SDL_MapRGB(icon->format, 255, 0, 255); - - SDL_CALL SDL_SetColorKey(icon, SDL_SRCCOLORKEY, rgbmap); - SDL_CALL SDL_WM_SetIcon(icon, NULL); - SDL_CALL SDL_FreeSurface(icon); - } - - // DO NOT CHANGE TO HWSURFACE, IT DOES NOT WORK - newscreen = SDL_CALL SDL_SetVideoMode(w, h, 8, SDL_SWSURFACE | SDL_HWPALETTE | (_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE)); - if (newscreen == NULL) - return false; - - _screen.width = newscreen->w; - _screen.height = newscreen->h; - _screen.pitch = newscreen->pitch; - - _sdl_screen = newscreen; - InitPalette(); - - snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); - SDL_CALL SDL_WM_SetCaption(caption, caption); - SDL_CALL SDL_ShowCursor(0); - - GameSizeChanged(); - - return true; -} - -typedef struct VkMapping { - uint16 vk_from; - byte vk_count; - byte map_to; -} VkMapping; - -#define AS(x, z) {x, 0, z} -#define AM(x, y, z, w) {x, y - x, z} - -static const VkMapping _vk_mapping[] = { - // Pageup stuff + up/down - AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN), - AS(SDLK_UP, WKC_UP), - AS(SDLK_DOWN, WKC_DOWN), - AS(SDLK_LEFT, WKC_LEFT), - AS(SDLK_RIGHT, WKC_RIGHT), - - AS(SDLK_HOME, WKC_HOME), - AS(SDLK_END, WKC_END), - - AS(SDLK_INSERT, WKC_INSERT), - AS(SDLK_DELETE, WKC_DELETE), - - // Map letters & digits - AM(SDLK_a, SDLK_z, 'A', 'Z'), - AM(SDLK_0, SDLK_9, '0', '9'), - - AS(SDLK_ESCAPE, WKC_ESC), - AS(SDLK_PAUSE, WKC_PAUSE), - AS(SDLK_BACKSPACE, WKC_BACKSPACE), - - AS(SDLK_SPACE, WKC_SPACE), - AS(SDLK_RETURN, WKC_RETURN), - AS(SDLK_TAB, WKC_TAB), - - // Function keys - AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12), - - // Numeric part. - // What is the virtual keycode for numeric enter?? - AM(SDLK_KP0, SDLK_KP9, WKC_NUM_0, WKC_NUM_9), - AS(SDLK_KP_DIVIDE, WKC_NUM_DIV), - AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL), - AS(SDLK_KP_MINUS, WKC_NUM_MINUS), - AS(SDLK_KP_PLUS, WKC_NUM_PLUS), - AS(SDLK_KP_ENTER, WKC_NUM_ENTER), - AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL) -}; - -static uint32 ConvertSdlKeyIntoMy(SDL_keysym *sym) -{ - const VkMapping *map; - uint key = 0; - - for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { - if ((uint)(sym->sym - map->vk_from) <= map->vk_count) { - key = sym->sym - map->vk_from + map->map_to; - break; - } - } - - // check scancode for BACKQUOTE key, because we want the key left of "1", not anything else (on non-US keyboards) -#if defined(WIN32) || defined(__OS2__) - if (sym->scancode == 41) key = WKC_BACKQUOTE; -#elif defined(__APPLE__) - if (sym->scancode == 10) key = WKC_BACKQUOTE; -#elif defined(__MORPHOS__) - if (sym->scancode == 0) key = WKC_BACKQUOTE; // yes, that key is code '0' under MorphOS :) -#elif defined(__BEOS__) - if (sym->scancode == 17) key = WKC_BACKQUOTE; -#elif defined(__SVR4) && defined(__sun) - if (sym->scancode == 60) key = WKC_BACKQUOTE; - if (sym->scancode == 49) key = WKC_BACKSPACE; -#elif defined(__sgi__) - if (sym->scancode == 22) key = WKC_BACKQUOTE; -#else - if (sym->scancode == 49) key = WKC_BACKQUOTE; -#endif - - // META are the command keys on mac - if (sym->mod & KMOD_META) key |= WKC_META; - if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT; - if (sym->mod & KMOD_CTRL) key |= WKC_CTRL; - if (sym->mod & KMOD_ALT) key |= WKC_ALT; - // these two lines really help porting hotkey combos. Uncomment to use -- Bjarni -#if 0 - DEBUG(driver, 0, "Scancode character pressed %u", sym->scancode); - DEBUG(driver, 0, "Unicode character pressed %u", sym->unicode); -#endif - return (key << 16) + sym->unicode; -} - -static int PollEvent(void) -{ - SDL_Event ev; - - if (!SDL_CALL SDL_PollEvent(&ev)) return -2; - - switch (ev.type) { - case SDL_MOUSEMOTION: - if (_cursor.fix_at) { - int dx = ev.motion.x - _cursor.pos.x; - int dy = ev.motion.y - _cursor.pos.y; - if (dx != 0 || dy != 0) { - _cursor.delta.x += dx; - _cursor.delta.y += dy; - SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y); - } - } else { - _cursor.delta.x = ev.motion.x - _cursor.pos.x; - _cursor.delta.y = ev.motion.y - _cursor.pos.y; - _cursor.pos.x = ev.motion.x; - _cursor.pos.y = ev.motion.y; - _cursor.dirty = true; - } - HandleMouseEvents(); - break; - - case SDL_MOUSEBUTTONDOWN: - if (_rightclick_emulate && SDL_CALL SDL_GetModState() & KMOD_CTRL) { - ev.button.button = SDL_BUTTON_RIGHT; - } - - switch (ev.button.button) { - case SDL_BUTTON_LEFT: - _left_button_down = true; - break; - - case SDL_BUTTON_RIGHT: - _right_button_down = true; - _right_button_clicked = true; - break; - - case SDL_BUTTON_WHEELUP: _cursor.wheel--; break; - case SDL_BUTTON_WHEELDOWN: _cursor.wheel++; break; - - default: break; - } - HandleMouseEvents(); - break; - - case SDL_MOUSEBUTTONUP: - if (_rightclick_emulate) { - _right_button_down = false; - _left_button_down = false; - _left_button_clicked = false; - } else if (ev.button.button == SDL_BUTTON_LEFT) { - _left_button_down = false; - _left_button_clicked = false; - } else if (ev.button.button == SDL_BUTTON_RIGHT) { - _right_button_down = false; - } - HandleMouseEvents(); - break; - - case SDL_ACTIVEEVENT: - if (!(ev.active.state & SDL_APPMOUSEFOCUS)) break; - - if (ev.active.gain) { // mouse entered the window, enable cursor - _cursor.in_window = true; - } else { - UndrawMouseCursor(); // mouse left the window, undraw cursor - _cursor.in_window = false; - } - break; - - case SDL_QUIT: HandleExitGameRequest(); break; - - case SDL_KEYDOWN: /* Toggle full-screen on ALT + ENTER/F */ - if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_META)) && - (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) { - ToggleFullScreen(!_fullscreen); - } else { - HandleKeypress(ConvertSdlKeyIntoMy(&ev.key.keysym)); - } - break; - - case SDL_VIDEORESIZE: { - int w = clamp(ev.resize.w, 64, MAX_SCREEN_WIDTH); - int h = clamp(ev.resize.h, 64, MAX_SCREEN_HEIGHT); - ChangeResInGame(w, h); - break; - } - } - return -1; -} - -static const char *SdlVideoStart(const char * const *parm) -{ - char buf[30]; - - const char *s = SdlOpen(SDL_INIT_VIDEO); - if (s != NULL) return s; - - SDL_CALL SDL_VideoDriverName(buf, 30); - DEBUG(driver, 1, "SDL: using driver '%s'", buf); - - GetVideoModes(); - CreateMainSurface(_cur_resolution[0], _cur_resolution[1]); - MarkWholeScreenDirty(); - - SDL_CALL SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); - SDL_CALL SDL_EnableUNICODE(1); - return NULL; -} - -static void SdlVideoStop(void) -{ - SdlClose(SDL_INIT_VIDEO); -} - -static void SdlVideoMainLoop(void) -{ - uint32 cur_ticks = SDL_CALL SDL_GetTicks(); - uint32 next_tick = cur_ticks + 30; - uint32 pal_tick = 0; - uint32 mod; - int numkeys; - Uint8 *keys; - - for (;;) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping - InteractiveRandom(); // randomness - - while (PollEvent() == -1) {} - if (_exit_game) return; - - mod = SDL_CALL SDL_GetModState(); - keys = SDL_CALL SDL_GetKeyState(&numkeys); -#if defined(_DEBUG) - if (_shift_pressed) -#else - /* Speedup when pressing tab, except when using ALT+TAB - * to switch to another application */ - if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0) -#endif - { - if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2; - } else if (_fast_forward & 2) { - _fast_forward = 0; - } - - cur_ticks = SDL_CALL SDL_GetTicks(); - if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) { - next_tick = cur_ticks + 30; - - _ctrl_pressed = !!(mod & KMOD_CTRL); - _shift_pressed = !!(mod & KMOD_SHIFT); -#ifdef _DEBUG - _dbg_screen_rect = !!(mod & KMOD_CAPS); -#endif - - // determine which directional keys are down - _dirkeys = - (keys[SDLK_LEFT] ? 1 : 0) | - (keys[SDLK_UP] ? 2 : 0) | - (keys[SDLK_RIGHT] ? 4 : 0) | - (keys[SDLK_DOWN] ? 8 : 0); - GameLoop(); - - _screen.dst_ptr = _sdl_screen->pixels; - UpdateWindows(); - if (++pal_tick > 4) { - CheckPaletteAnim(); - pal_tick = 1; - } - DrawSurfaceToScreen(); - } else { - SDL_CALL SDL_Delay(1); - _screen.dst_ptr = _sdl_screen->pixels; - DrawTextMessage(); - DrawMouseCursor(); - DrawSurfaceToScreen(); - } - } -} - -static bool SdlVideoChangeRes(int w, int h) -{ - return CreateMainSurface(w, h); -} - -static void SdlVideoFullScreen(bool full_screen) -{ - _fullscreen = full_screen; - GetVideoModes(); // get the list of available video modes - if (_num_resolutions == 0 || !_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1])) { - // switching resolution failed, put back full_screen to original status - _fullscreen ^= true; - } -} - -const HalVideoDriver _sdl_video_driver = { - SdlVideoStart, - SdlVideoStop, - SdlVideoMakeDirty, - SdlVideoMainLoop, - SdlVideoChangeRes, - SdlVideoFullScreen, -}; - -#endif diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp new file mode 100644 index 000000000..3e776d29f --- /dev/null +++ b/src/video/sdl_v.cpp @@ -0,0 +1,513 @@ +/* $Id$ */ + +#include "../stdafx.h" + +#ifdef WITH_SDL + +#include "../openttd.h" +#include "../debug.h" +#include "../functions.h" +#include "../gfx.h" +#include "../macros.h" +#include "../sdl.h" +#include "../window.h" +#include "../network/network.h" +#include "../variables.h" +#include "sdl_v.h" +#include + +static SDL_Surface *_sdl_screen; +static bool _all_modes; + +#define MAX_DIRTY_RECTS 100 +static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS]; +static int _num_dirty_rects; + +static void SdlVideoMakeDirty(int left, int top, int width, int height) +{ + if (_num_dirty_rects < MAX_DIRTY_RECTS) { + _dirty_rects[_num_dirty_rects].x = left; + _dirty_rects[_num_dirty_rects].y = top; + _dirty_rects[_num_dirty_rects].w = width; + _dirty_rects[_num_dirty_rects].h = height; + } + _num_dirty_rects++; +} + +static void UpdatePalette(uint start, uint count) +{ + SDL_Color pal[256]; + uint i; + + for (i = 0; i != count; i++) { + pal[i].r = _cur_palette[start + i].r; + pal[i].g = _cur_palette[start + i].g; + pal[i].b = _cur_palette[start + i].b; + pal[i].unused = 0; + } + + SDL_CALL SDL_SetColors(_sdl_screen, pal, start, count); +} + +static void InitPalette(void) +{ + UpdatePalette(0, 256); +} + +static void CheckPaletteAnim(void) +{ + if (_pal_last_dirty != -1) { + UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); + _pal_last_dirty = -1; + } +} + +static void DrawSurfaceToScreen(void) +{ + int n = _num_dirty_rects; + if (n != 0) { + _num_dirty_rects = 0; + if (n > MAX_DIRTY_RECTS) + SDL_CALL SDL_UpdateRect(_sdl_screen, 0, 0, 0, 0); + else + SDL_CALL SDL_UpdateRects(_sdl_screen, n, _dirty_rects); + } +} + +static const uint16 default_resolutions[][2] = { + { 640, 480}, + { 800, 600}, + {1024, 768}, + {1152, 864}, + {1280, 800}, + {1280, 960}, + {1280, 1024}, + {1400, 1050}, + {1600, 1200}, + {1680, 1050}, + {1920, 1200} +}; + +static void GetVideoModes(void) +{ + int i; + SDL_Rect **modes; + + modes = SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE + (_fullscreen ? SDL_FULLSCREEN : 0)); + + if (modes == NULL) + error("sdl: no modes available"); + + _all_modes = (modes == (void*)-1); + + if (_all_modes) { + // all modes available, put some default ones here + memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); + _num_resolutions = lengthof(default_resolutions); + } else { + int n = 0; + for (i = 0; modes[i]; i++) { + int w = modes[i]->w; + int h = modes[i]->h; + if (IS_INT_INSIDE(w, 640, MAX_SCREEN_WIDTH + 1) && + IS_INT_INSIDE(h, 480, MAX_SCREEN_HEIGHT + 1)) { + int j; + for (j = 0; j < n; j++) { + if (_resolutions[j][0] == w && _resolutions[j][1] == h) break; + } + + if (j == n) { + _resolutions[j][0] = w; + _resolutions[j][1] = h; + if (++n == lengthof(_resolutions)) break; + } + } + } + _num_resolutions = n; + SortResolutions(_num_resolutions); + } +} + +static void GetAvailableVideoMode(int *w, int *h) +{ + int i; + int best; + uint delta; + + // all modes available? + if (_all_modes) return; + + // is the wanted mode among the available modes? + for (i = 0; i != _num_resolutions; i++) { + if (*w == _resolutions[i][0] && *h == _resolutions[i][1]) return; + } + + // use the closest possible resolution + best = 0; + delta = abs((_resolutions[0][0] - *w) * (_resolutions[0][1] - *h)); + for (i = 1; i != _num_resolutions; ++i) { + uint newdelta = abs((_resolutions[i][0] - *w) * (_resolutions[i][1] - *h)); + if (newdelta < delta) { + best = i; + delta = newdelta; + } + } + *w = _resolutions[best][0]; + *h = _resolutions[best][1]; +} + +#ifndef ICON_DIR +#define ICON_DIR "media" +#endif + +#ifdef WIN32 +/* Let's redefine the LoadBMP macro with because we are dynamically + * loading SDL and need to 'SDL_CALL' all functions */ +#undef SDL_LoadBMP +#define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_CALL SDL_RWFromFile(file, "rb"), 1) +#endif + +static bool CreateMainSurface(int w, int h) +{ + extern const char _openttd_revision[]; + SDL_Surface *newscreen, *icon; + char caption[50]; + + GetAvailableVideoMode(&w, &h); + + DEBUG(driver, 1, "SDL: using mode %dx%d", w, h); + + /* Give the application an icon */ + icon = SDL_CALL SDL_LoadBMP(ICON_DIR PATHSEP "openttd.32.bmp"); + if (icon != NULL) { + /* Get the colourkey, which will be magenta */ + uint32 rgbmap = SDL_CALL SDL_MapRGB(icon->format, 255, 0, 255); + + SDL_CALL SDL_SetColorKey(icon, SDL_SRCCOLORKEY, rgbmap); + SDL_CALL SDL_WM_SetIcon(icon, NULL); + SDL_CALL SDL_FreeSurface(icon); + } + + // DO NOT CHANGE TO HWSURFACE, IT DOES NOT WORK + newscreen = SDL_CALL SDL_SetVideoMode(w, h, 8, SDL_SWSURFACE | SDL_HWPALETTE | (_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE)); + if (newscreen == NULL) + return false; + + _screen.width = newscreen->w; + _screen.height = newscreen->h; + _screen.pitch = newscreen->pitch; + + _sdl_screen = newscreen; + InitPalette(); + + snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); + SDL_CALL SDL_WM_SetCaption(caption, caption); + SDL_CALL SDL_ShowCursor(0); + + GameSizeChanged(); + + return true; +} + +typedef struct VkMapping { + uint16 vk_from; + byte vk_count; + byte map_to; +} VkMapping; + +#define AS(x, z) {x, 0, z} +#define AM(x, y, z, w) {x, y - x, z} + +static const VkMapping _vk_mapping[] = { + // Pageup stuff + up/down + AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN), + AS(SDLK_UP, WKC_UP), + AS(SDLK_DOWN, WKC_DOWN), + AS(SDLK_LEFT, WKC_LEFT), + AS(SDLK_RIGHT, WKC_RIGHT), + + AS(SDLK_HOME, WKC_HOME), + AS(SDLK_END, WKC_END), + + AS(SDLK_INSERT, WKC_INSERT), + AS(SDLK_DELETE, WKC_DELETE), + + // Map letters & digits + AM(SDLK_a, SDLK_z, 'A', 'Z'), + AM(SDLK_0, SDLK_9, '0', '9'), + + AS(SDLK_ESCAPE, WKC_ESC), + AS(SDLK_PAUSE, WKC_PAUSE), + AS(SDLK_BACKSPACE, WKC_BACKSPACE), + + AS(SDLK_SPACE, WKC_SPACE), + AS(SDLK_RETURN, WKC_RETURN), + AS(SDLK_TAB, WKC_TAB), + + // Function keys + AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12), + + // Numeric part. + // What is the virtual keycode for numeric enter?? + AM(SDLK_KP0, SDLK_KP9, WKC_NUM_0, WKC_NUM_9), + AS(SDLK_KP_DIVIDE, WKC_NUM_DIV), + AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL), + AS(SDLK_KP_MINUS, WKC_NUM_MINUS), + AS(SDLK_KP_PLUS, WKC_NUM_PLUS), + AS(SDLK_KP_ENTER, WKC_NUM_ENTER), + AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL) +}; + +static uint32 ConvertSdlKeyIntoMy(SDL_keysym *sym) +{ + const VkMapping *map; + uint key = 0; + + for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { + if ((uint)(sym->sym - map->vk_from) <= map->vk_count) { + key = sym->sym - map->vk_from + map->map_to; + break; + } + } + + // check scancode for BACKQUOTE key, because we want the key left of "1", not anything else (on non-US keyboards) +#if defined(WIN32) || defined(__OS2__) + if (sym->scancode == 41) key = WKC_BACKQUOTE; +#elif defined(__APPLE__) + if (sym->scancode == 10) key = WKC_BACKQUOTE; +#elif defined(__MORPHOS__) + if (sym->scancode == 0) key = WKC_BACKQUOTE; // yes, that key is code '0' under MorphOS :) +#elif defined(__BEOS__) + if (sym->scancode == 17) key = WKC_BACKQUOTE; +#elif defined(__SVR4) && defined(__sun) + if (sym->scancode == 60) key = WKC_BACKQUOTE; + if (sym->scancode == 49) key = WKC_BACKSPACE; +#elif defined(__sgi__) + if (sym->scancode == 22) key = WKC_BACKQUOTE; +#else + if (sym->scancode == 49) key = WKC_BACKQUOTE; +#endif + + // META are the command keys on mac + if (sym->mod & KMOD_META) key |= WKC_META; + if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT; + if (sym->mod & KMOD_CTRL) key |= WKC_CTRL; + if (sym->mod & KMOD_ALT) key |= WKC_ALT; + // these two lines really help porting hotkey combos. Uncomment to use -- Bjarni +#if 0 + DEBUG(driver, 0, "Scancode character pressed %u", sym->scancode); + DEBUG(driver, 0, "Unicode character pressed %u", sym->unicode); +#endif + return (key << 16) + sym->unicode; +} + +static int PollEvent(void) +{ + SDL_Event ev; + + if (!SDL_CALL SDL_PollEvent(&ev)) return -2; + + switch (ev.type) { + case SDL_MOUSEMOTION: + if (_cursor.fix_at) { + int dx = ev.motion.x - _cursor.pos.x; + int dy = ev.motion.y - _cursor.pos.y; + if (dx != 0 || dy != 0) { + _cursor.delta.x += dx; + _cursor.delta.y += dy; + SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y); + } + } else { + _cursor.delta.x = ev.motion.x - _cursor.pos.x; + _cursor.delta.y = ev.motion.y - _cursor.pos.y; + _cursor.pos.x = ev.motion.x; + _cursor.pos.y = ev.motion.y; + _cursor.dirty = true; + } + HandleMouseEvents(); + break; + + case SDL_MOUSEBUTTONDOWN: + if (_rightclick_emulate && SDL_CALL SDL_GetModState() & KMOD_CTRL) { + ev.button.button = SDL_BUTTON_RIGHT; + } + + switch (ev.button.button) { + case SDL_BUTTON_LEFT: + _left_button_down = true; + break; + + case SDL_BUTTON_RIGHT: + _right_button_down = true; + _right_button_clicked = true; + break; + + case SDL_BUTTON_WHEELUP: _cursor.wheel--; break; + case SDL_BUTTON_WHEELDOWN: _cursor.wheel++; break; + + default: break; + } + HandleMouseEvents(); + break; + + case SDL_MOUSEBUTTONUP: + if (_rightclick_emulate) { + _right_button_down = false; + _left_button_down = false; + _left_button_clicked = false; + } else if (ev.button.button == SDL_BUTTON_LEFT) { + _left_button_down = false; + _left_button_clicked = false; + } else if (ev.button.button == SDL_BUTTON_RIGHT) { + _right_button_down = false; + } + HandleMouseEvents(); + break; + + case SDL_ACTIVEEVENT: + if (!(ev.active.state & SDL_APPMOUSEFOCUS)) break; + + if (ev.active.gain) { // mouse entered the window, enable cursor + _cursor.in_window = true; + } else { + UndrawMouseCursor(); // mouse left the window, undraw cursor + _cursor.in_window = false; + } + break; + + case SDL_QUIT: HandleExitGameRequest(); break; + + case SDL_KEYDOWN: /* Toggle full-screen on ALT + ENTER/F */ + if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_META)) && + (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) { + ToggleFullScreen(!_fullscreen); + } else { + HandleKeypress(ConvertSdlKeyIntoMy(&ev.key.keysym)); + } + break; + + case SDL_VIDEORESIZE: { + int w = clamp(ev.resize.w, 64, MAX_SCREEN_WIDTH); + int h = clamp(ev.resize.h, 64, MAX_SCREEN_HEIGHT); + ChangeResInGame(w, h); + break; + } + } + return -1; +} + +static const char *SdlVideoStart(const char * const *parm) +{ + char buf[30]; + + const char *s = SdlOpen(SDL_INIT_VIDEO); + if (s != NULL) return s; + + SDL_CALL SDL_VideoDriverName(buf, 30); + DEBUG(driver, 1, "SDL: using driver '%s'", buf); + + GetVideoModes(); + CreateMainSurface(_cur_resolution[0], _cur_resolution[1]); + MarkWholeScreenDirty(); + + SDL_CALL SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + SDL_CALL SDL_EnableUNICODE(1); + return NULL; +} + +static void SdlVideoStop(void) +{ + SdlClose(SDL_INIT_VIDEO); +} + +static void SdlVideoMainLoop(void) +{ + uint32 cur_ticks = SDL_CALL SDL_GetTicks(); + uint32 next_tick = cur_ticks + 30; + uint32 pal_tick = 0; + uint32 mod; + int numkeys; + Uint8 *keys; + + for (;;) { + uint32 prev_cur_ticks = cur_ticks; // to check for wrapping + InteractiveRandom(); // randomness + + while (PollEvent() == -1) {} + if (_exit_game) return; + + mod = SDL_CALL SDL_GetModState(); + keys = SDL_CALL SDL_GetKeyState(&numkeys); +#if defined(_DEBUG) + if (_shift_pressed) +#else + /* Speedup when pressing tab, except when using ALT+TAB + * to switch to another application */ + if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0) +#endif + { + if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2; + } else if (_fast_forward & 2) { + _fast_forward = 0; + } + + cur_ticks = SDL_CALL SDL_GetTicks(); + if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) { + next_tick = cur_ticks + 30; + + _ctrl_pressed = !!(mod & KMOD_CTRL); + _shift_pressed = !!(mod & KMOD_SHIFT); +#ifdef _DEBUG + _dbg_screen_rect = !!(mod & KMOD_CAPS); +#endif + + // determine which directional keys are down + _dirkeys = + (keys[SDLK_LEFT] ? 1 : 0) | + (keys[SDLK_UP] ? 2 : 0) | + (keys[SDLK_RIGHT] ? 4 : 0) | + (keys[SDLK_DOWN] ? 8 : 0); + GameLoop(); + + _screen.dst_ptr = _sdl_screen->pixels; + UpdateWindows(); + if (++pal_tick > 4) { + CheckPaletteAnim(); + pal_tick = 1; + } + DrawSurfaceToScreen(); + } else { + SDL_CALL SDL_Delay(1); + _screen.dst_ptr = _sdl_screen->pixels; + DrawTextMessage(); + DrawMouseCursor(); + DrawSurfaceToScreen(); + } + } +} + +static bool SdlVideoChangeRes(int w, int h) +{ + return CreateMainSurface(w, h); +} + +static void SdlVideoFullScreen(bool full_screen) +{ + _fullscreen = full_screen; + GetVideoModes(); // get the list of available video modes + if (_num_resolutions == 0 || !_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1])) { + // switching resolution failed, put back full_screen to original status + _fullscreen ^= true; + } +} + +const HalVideoDriver _sdl_video_driver = { + SdlVideoStart, + SdlVideoStop, + SdlVideoMakeDirty, + SdlVideoMainLoop, + SdlVideoChangeRes, + SdlVideoFullScreen, +}; + +#endif diff --git a/src/video/win32_v.c b/src/video/win32_v.c deleted file mode 100644 index eab832ae5..000000000 --- a/src/video/win32_v.c +++ /dev/null @@ -1,876 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" -#include "../openttd.h" -#include "../functions.h" -#include "../gfx.h" -#include "../macros.h" -#include "../network/network.h" -#include "../variables.h" -#include "../win32.h" -#include "../window.h" -#include "win32_v.h" -#include -#include - -static struct { - HWND main_wnd; - HBITMAP dib_sect; - Pixel *bitmap_bits; - Pixel *buffer_bits; - Pixel *alloced_bits; - HPALETTE gdi_palette; - int width; - int height; - int width_org; - int height_org; - bool fullscreen; - bool double_size; - bool has_focus; - bool running; -} _wnd; - -bool _force_full_redraw; -bool _double_size; -bool _window_maximize; -uint _display_hz; -uint _fullscreen_bpp; -static uint16 _bck_resolution[2]; - -static void MakePalette(void) -{ - LOGPALETTE *pal; - uint i; - - pal = alloca(sizeof(LOGPALETTE) + (256-1) * sizeof(PALETTEENTRY)); - - pal->palVersion = 0x300; - pal->palNumEntries = 256; - - for (i = 0; i != 256; i++) { - pal->palPalEntry[i].peRed = _cur_palette[i].r; - pal->palPalEntry[i].peGreen = _cur_palette[i].g; - pal->palPalEntry[i].peBlue = _cur_palette[i].b; - pal->palPalEntry[i].peFlags = 0; - - } - _wnd.gdi_palette = CreatePalette(pal); - if (_wnd.gdi_palette == NULL) error("CreatePalette failed!\n"); -} - -static void UpdatePalette(HDC dc, uint start, uint count) -{ - RGBQUAD rgb[256]; - uint i; - - for (i = 0; i != count; i++) { - rgb[i].rgbRed = _cur_palette[start + i].r; - rgb[i].rgbGreen = _cur_palette[start + i].g; - rgb[i].rgbBlue = _cur_palette[start + i].b; - rgb[i].rgbReserved = 0; - } - - SetDIBColorTable(dc, start, count, rgb); -} - -typedef struct { - byte vk_from; - byte vk_count; - byte map_to; -} VkMapping; - -#define AS(x, z) {x, 0, z} -#define AM(x, y, z, w) {x, y - x, z} - -static const VkMapping _vk_mapping[] = { - // Pageup stuff + up/down - AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN), - // Map letters & digits - AM('A','Z','A','Z'), - AM('0','9','0','9'), - - AS(VK_ESCAPE, WKC_ESC), - AS(VK_PAUSE, WKC_PAUSE), - AS(VK_BACK, WKC_BACKSPACE), - AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE), - - AS(VK_SPACE, WKC_SPACE), - AS(VK_RETURN, WKC_RETURN), - AS(VK_TAB, WKC_TAB), - - // Function keys - AM(VK_F1, VK_F12, WKC_F1, WKC_F12), - - // Numeric part. - // What is the virtual keycode for numeric enter?? - AM(VK_NUMPAD0, VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9), - AS(VK_DIVIDE, WKC_NUM_DIV), - AS(VK_MULTIPLY, WKC_NUM_MUL), - AS(VK_SUBTRACT, WKC_NUM_MINUS), - AS(VK_ADD, WKC_NUM_PLUS), - AS(VK_DECIMAL, WKC_NUM_DECIMAL) -}; - -static uint MapWindowsKey(uint sym) -{ - const VkMapping *map; - uint key = 0; - - for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { - if ((uint)(sym - map->vk_from) <= map->vk_count) { - key = sym - map->vk_from + map->map_to; - break; - } - } - - if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT; - if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL; - if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT; - return key; -} - -static bool AllocateDibSection(int w, int h); - -static void ClientSizeChanged(int w, int h) -{ - if (_wnd.double_size) { - w /= 2; - h /= 2; - } - - // allocate new dib section of the new size - if (AllocateDibSection(w, h)) { - // mark all palette colors dirty - _pal_first_dirty = 0; - _pal_last_dirty = 255; - GameSizeChanged(); - - // redraw screen - if (_wnd.running) { - _screen.dst_ptr = _wnd.buffer_bits; - UpdateWindows(); - } - } -} - -#ifdef _DEBUG -// Keep this function here.. -// It allows you to redraw the screen from within the MSVC debugger -int RedrawScreenDebug(void) -{ - HDC dc,dc2; - static int _fooctr; - HBITMAP old_bmp; - HPALETTE old_palette; - - _screen.dst_ptr = _wnd.buffer_bits; - UpdateWindows(); - - dc = GetDC(_wnd.main_wnd); - dc2 = CreateCompatibleDC(dc); - - old_bmp = SelectObject(dc2, _wnd.dib_sect); - old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); - BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); - SelectPalette(dc, old_palette, TRUE); - SelectObject(dc2, old_bmp); - DeleteDC(dc2); - ReleaseDC(_wnd.main_wnd, dc); - - return _fooctr++; -} -#endif - -/* Windows 95 will not have a WM_MOUSELEAVE message, so define it if needed */ -#if !defined(WM_MOUSELEAVE) -#define WM_MOUSELEAVE 0x02A3 -#endif -#define TID_POLLMOUSE 1 -#define MOUSE_POLL_DELAY 75 - -static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time) -{ - RECT rc; - POINT pt; - - /* Get the rectangle of our window and translate it to screen coordinates. - * Compare this with the current screen coordinates of the mouse and if it - * falls outside of the area or our window we have left the window. */ - GetClientRect(hwnd, &rc); - MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2); - GetCursorPos(&pt); - - if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) { - KillTimer(hwnd, event); - PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L); - } -} - -static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_CREATE: - SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); - break; - - case WM_PAINT: { - PAINTSTRUCT ps; - HDC dc,dc2; - HBITMAP old_bmp; - HPALETTE old_palette; - - BeginPaint(hwnd, &ps); - dc = ps.hdc; - dc2 = CreateCompatibleDC(dc); - old_bmp = SelectObject(dc2, _wnd.dib_sect); - old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); - - if (_pal_last_dirty != -1) { - UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); - _pal_last_dirty = -1; - } - - BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); - SelectPalette(dc, old_palette, TRUE); - SelectObject(dc2, old_bmp); - DeleteDC(dc2); - EndPaint(hwnd, &ps); - return 0; - } - - case WM_PALETTECHANGED: - if ((HWND)wParam == hwnd) return 0; - /* FALLTHROUGH */ - - case WM_QUERYNEWPALETTE: { - HDC hDC = GetWindowDC(hwnd); - HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE); - UINT nChanged = RealizePalette(hDC); - - SelectPalette(hDC, hOldPalette, TRUE); - ReleaseDC(hwnd, hDC); - if (nChanged) InvalidateRect(hwnd, NULL, FALSE); - return 0; - } - - case WM_CLOSE: - HandleExitGameRequest(); - return 0; - - case WM_DESTROY: - if (_window_maximize) { - _cur_resolution[0] = _bck_resolution[0]; - _cur_resolution[1] = _bck_resolution[1]; - } - return 0; - - case WM_LBUTTONDOWN: - SetCapture(hwnd); - _left_button_down = true; - HandleMouseEvents(); - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - _left_button_down = false; - _left_button_clicked = false; - HandleMouseEvents(); - return 0; - - case WM_RBUTTONDOWN: - SetCapture(hwnd); - _right_button_down = true; - _right_button_clicked = true; - HandleMouseEvents(); - return 0; - - case WM_RBUTTONUP: - ReleaseCapture(); - _right_button_down = false; - HandleMouseEvents(); - return 0; - - case WM_MOUSELEAVE: - UndrawMouseCursor(); - _cursor.in_window = false; - - if (!_left_button_down && !_right_button_down) MyShowCursor(true); - HandleMouseEvents(); - return 0; - - case WM_MOUSEMOVE: { - int x = (int16)LOWORD(lParam); - int y = (int16)HIWORD(lParam); - POINT pt; - - /* If the mouse was not in the window and it has moved it means it has - * come into the window, so start drawing the mouse. Also start - * tracking the mouse for exiting the window */ - if (!_cursor.in_window) { - _cursor.in_window = true; - SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); - - DrawMouseCursor(); - } - - if (_wnd.double_size) { - x /= 2; - y /= 2; - } - - if (_cursor.fix_at) { - int dx = x - _cursor.pos.x; - int dy = y - _cursor.pos.y; - if (dx != 0 || dy != 0) { - _cursor.delta.x += dx; - _cursor.delta.y += dy; - - pt.x = _cursor.pos.x; - pt.y = _cursor.pos.y; - - if (_wnd.double_size) { - pt.x *= 2; - pt.y *= 2; - } - ClientToScreen(hwnd, &pt); - SetCursorPos(pt.x, pt.y); - } - } else { - _cursor.delta.x += x - _cursor.pos.x; - _cursor.delta.y += y - _cursor.pos.y; - _cursor.pos.x = x; - _cursor.pos.y = y; - _cursor.dirty = true; - } - MyShowCursor(false); - HandleMouseEvents(); - return 0; - } - - case WM_KEYDOWN: { - // this is the rewritten ascii input function - // it disables windows deadkey handling --> more linux like :D - wchar_t w = 0; - byte ks[256]; - uint scancode; - uint32 pressed_key; - - GetKeyboardState(ks); - if (ToUnicode(wParam, 0, ks, &w, 1, 0) != 1) { - /* On win9x ToUnicode always fails, so fall back to ToAscii */ - if (ToAscii(wParam, 0, ks, &w, 0) != 1) w = 0; // no translation was possible - } - - pressed_key = w | MapWindowsKey(wParam) << 16; - - scancode = GB(lParam, 16, 8); - if (scancode == 41) pressed_key = w | WKC_BACKQUOTE << 16; - - if (GB(pressed_key, 16, 16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) { - _double_size ^= 1; - _wnd.double_size = _double_size; - ClientSizeChanged(_wnd.width, _wnd.height); - MarkWholeScreenDirty(); - } - HandleKeypress(pressed_key); - break; - } - - case WM_SYSKEYDOWN: /* user presses F10 or Alt, both activating the title-menu */ - switch (wParam) { - case VK_RETURN: - case 'F': /* Full Screen on ALT + ENTER/F */ - ToggleFullScreen(!_wnd.fullscreen); - return 0; - - case VK_MENU: /* Just ALT */ - return 0; // do nothing - - case VK_F10: /* F10, ignore activation of menu */ - HandleKeypress(MapWindowsKey(wParam) << 16); - return 0; - - default: /* ALT in combination with something else */ - HandleKeypress(MapWindowsKey(wParam) << 16); - break; - } - break; - - case WM_SIZE: - if (wParam != SIZE_MINIMIZED) { - /* Set maximized flag when we maximize (obviously), but also when we - * switched to fullscreen from a maximized state */ - _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen)); - if (_window_maximize) { - _bck_resolution[0] = _cur_resolution[0]; - _bck_resolution[1] = _cur_resolution[1]; - } - ClientSizeChanged(LOWORD(lParam), HIWORD(lParam)); - } - return 0; - - case WM_SIZING: { - RECT* r = (RECT*)lParam; - RECT r2; - int w, h; - - SetRect(&r2, 0, 0, 0, 0); - AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); - - w = r->right - r->left - (r2.right - r2.left); - h = r->bottom - r->top - (r2.bottom - r2.top); - if (_wnd.double_size) { - w /= 2; - h /= 2; - } - w = clamp(w, 64, MAX_SCREEN_WIDTH); - h = clamp(h, 64, MAX_SCREEN_HEIGHT); - if (_wnd.double_size) { - w *= 2; - h *= 2; - } - SetRect(&r2, 0, 0, w, h); - - AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); - w = r2.right - r2.left; - h = r2.bottom - r2.top; - - switch (wParam) { - case WMSZ_BOTTOM: - r->bottom = r->top + h; - break; - - case WMSZ_BOTTOMLEFT: - r->bottom = r->top + h; - r->left = r->right - w; - break; - - case WMSZ_BOTTOMRIGHT: - r->bottom = r->top + h; - r->right = r->left + w; - break; - - case WMSZ_LEFT: - r->left = r->right - w; - break; - - case WMSZ_RIGHT: - r->right = r->left + w; - break; - - case WMSZ_TOP: - r->top = r->bottom - h; - break; - - case WMSZ_TOPLEFT: - r->top = r->bottom - h; - r->left = r->right - w; - break; - - case WMSZ_TOPRIGHT: - r->top = r->bottom - h; - r->right = r->left + w; - break; - } - return TRUE; - } - -// needed for wheel -#if !defined(WM_MOUSEWHEEL) -# define WM_MOUSEWHEEL 0x020A -#endif //WM_MOUSEWHEEL -#if !defined(GET_WHEEL_DELTA_WPARAM) -# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam)) -#endif //GET_WHEEL_DELTA_WPARAM - - case WM_MOUSEWHEEL: { - int delta = GET_WHEEL_DELTA_WPARAM(wParam); - - if (delta < 0) { - _cursor.wheel++; - } else if (delta > 0) { - _cursor.wheel--; - } - HandleMouseEvents(); - return 0; - } - - case WM_ACTIVATEAPP: - _wnd.has_focus = (bool)wParam; - break; - } - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -static void RegisterWndClass(void) -{ - static bool registered = false; - - if (!registered) { - HINSTANCE hinst = GetModuleHandle(NULL); - WNDCLASS wnd = { - 0, - WndProcGdi, - 0, - 0, - hinst, - LoadIcon(hinst, MAKEINTRESOURCE(100)), - LoadCursor(NULL, IDC_ARROW), - 0, - 0, - _T("OTTD") - }; - - registered = true; - if (!RegisterClass(&wnd)) error("RegisterClass failed"); - } -} - -static void MakeWindow(bool full_screen) -{ - _fullscreen = full_screen; - - _wnd.double_size = _double_size && !full_screen; - - // recreate window? - if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) { - DestroyWindow(_wnd.main_wnd); - _wnd.main_wnd = 0; - } - - if (full_screen) { - DEVMODE settings; - - memset(&settings, 0, sizeof(settings)); - settings.dmSize = sizeof(settings); - settings.dmFields = - (_fullscreen_bpp != 0 ? DM_BITSPERPEL : 0) | - DM_PELSWIDTH | - DM_PELSHEIGHT | - (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0); - settings.dmBitsPerPel = _fullscreen_bpp; - settings.dmPelsWidth = _wnd.width_org; - settings.dmPelsHeight = _wnd.height_org; - settings.dmDisplayFrequency = _display_hz; - - if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { - MakeWindow(false); - return; - } - } else if (_wnd.fullscreen) { - // restore display? - ChangeDisplaySettings(NULL, 0); - } - - { - RECT r; - DWORD style, showstyle; - int x, y, w, h; - - showstyle = SW_SHOWNORMAL; - _wnd.fullscreen = full_screen; - if (_wnd.fullscreen) { - style = WS_POPUP; - SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org); - } else { - style = WS_OVERLAPPEDWINDOW; - /* On window creation, check if we were in maximize mode before */ - if (_window_maximize) showstyle = SW_SHOWMAXIMIZED; - SetRect(&r, 0, 0, _wnd.width, _wnd.height); - } - - AdjustWindowRect(&r, style, FALSE); - w = r.right - r.left; - h = r.bottom - r.top; - x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; - y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; - - if (_wnd.main_wnd) { - ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL); // remove maximize-flag - SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); - } else { - extern const char _openttd_revision[]; - TCHAR Windowtitle[50]; - - _sntprintf(Windowtitle, sizeof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision)); - - _wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); - if (_wnd.main_wnd == NULL) error("CreateWindow failed"); - ShowWindow(_wnd.main_wnd, showstyle); - } - } - GameSizeChanged(); // invalidate all windows, force redraw -} - -static bool AllocateDibSection(int w, int h) -{ - BITMAPINFO *bi; - HDC dc; - - w = clamp(w, 64, MAX_SCREEN_WIDTH); - h = clamp(h, 64, MAX_SCREEN_HEIGHT); - - if (w == _screen.width && h == _screen.height) - return false; - - _screen.width = w; - _screen.pitch = ALIGN(w, 4); - _screen.height = h; - - if (_wnd.alloced_bits) { - free(_wnd.alloced_bits); - _wnd.alloced_bits = NULL; - } - - bi = alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); - memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); - bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - - if (_wnd.double_size) { - w = ALIGN(w, 4); - _wnd.alloced_bits = _wnd.buffer_bits = malloc(w * h); - w *= 2; - h *= 2; - } - - bi->bmiHeader.biWidth = _wnd.width = w; - bi->bmiHeader.biHeight = -(_wnd.height = h); - - bi->bmiHeader.biPlanes = 1; - bi->bmiHeader.biBitCount = 8; - bi->bmiHeader.biCompression = BI_RGB; - - if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect); - - dc = GetDC(0); - _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.bitmap_bits, NULL, 0); - if (_wnd.dib_sect == NULL) error("CreateDIBSection failed"); - ReleaseDC(0, dc); - - if (!_wnd.double_size) _wnd.buffer_bits = _wnd.bitmap_bits; - - return true; -} - -static const uint16 default_resolutions[][2] = { - { 640, 480 }, - { 800, 600 }, - { 1024, 768 }, - { 1152, 864 }, - { 1280, 800 }, - { 1280, 960 }, - { 1280, 1024 }, - { 1400, 1050 }, - { 1600, 1200 }, - { 1680, 1050 }, - { 1920, 1200 } -}; - -static void FindResolutions(void) -{ - uint n = 0; - uint i; - DEVMODEA dm; - - /* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95 - * Doesn't really matter since we don't pass a string anyways, but still - * a letdown */ - for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) { - if (dm.dmBitsPerPel == 8 && IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) && - IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)) { - uint j; - - for (j = 0; j < n; j++) { - if (_resolutions[j][0] == dm.dmPelsWidth && _resolutions[j][1] == dm.dmPelsHeight) break; - } - - /* In the previous loop we have checked already existing/added resolutions if - * they are the same as the new ones. If this is not the case (j == n); we have - * looped all and found none, add the new one to the list. If we have reached the - * maximum amount of resolutions, then quit querying the display */ - if (j == n) { - _resolutions[j][0] = dm.dmPelsWidth; - _resolutions[j][1] = dm.dmPelsHeight; - if (++n == lengthof(_resolutions)) break; - } - } - } - - /* We have found no resolutions, show the default list */ - if (n == 0) { - memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); - n = lengthof(default_resolutions); - } - - _num_resolutions = n; - SortResolutions(_num_resolutions); -} - - -static const char *Win32GdiStart(const char * const *parm) -{ - memset(&_wnd, 0, sizeof(_wnd)); - - RegisterWndClass(); - - MakePalette(); - - FindResolutions(); - - // fullscreen uses those - _wnd.width_org = _cur_resolution[0]; - _wnd.height_org = _cur_resolution[1]; - - AllocateDibSection(_cur_resolution[0], _cur_resolution[1]); - MarkWholeScreenDirty(); - - MakeWindow(_fullscreen); - - return NULL; -} - -static void Win32GdiStop(void) -{ - DeleteObject(_wnd.gdi_palette); - DeleteObject(_wnd.dib_sect); - DestroyWindow(_wnd.main_wnd); - - if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0); - if (_double_size) { - _cur_resolution[0] *= 2; - _cur_resolution[1] *= 2; - } - - MyShowCursor(true); -} - -// simple upscaler by 2 -static void filter(int left, int top, int width, int height) -{ - uint p = _screen.pitch; - const Pixel *s = _wnd.buffer_bits + top * p + left; - Pixel *d = _wnd.bitmap_bits + top * p * 4 + left * 2; - - for (; height > 0; height--) { - int i; - - for (i = 0; i != width; i++) { - d[i * 2] = d[i * 2 + 1] = d[i * 2 + p * 2] = d[i * 2 + 1 + p * 2] = s[i]; - } - s += p; - d += p * 4; - } -} - -static void Win32GdiMakeDirty(int left, int top, int width, int height) -{ - RECT r = { left, top, left + width, top + height }; - - if (_wnd.double_size) { - filter(left, top, width, height); - r.left *= 2; - r.top *= 2; - r.right *= 2; - r.bottom *= 2; - } - InvalidateRect(_wnd.main_wnd, &r, FALSE); -} - -static void CheckPaletteAnim(void) -{ - if (_pal_last_dirty == -1) - return; - InvalidateRect(_wnd.main_wnd, NULL, FALSE); -} - -static void Win32GdiMainLoop(void) -{ - MSG mesg; - uint32 cur_ticks = GetTickCount(); - uint32 next_tick = cur_ticks + 30; - - _wnd.running = true; - - for (;;) { - uint32 prev_cur_ticks = cur_ticks; // to check for wrapping - - while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) { - InteractiveRandom(); // randomness - DispatchMessage(&mesg); - } - if (_exit_game) return; - -#if defined(_DEBUG) - if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 && -#else - /* Speed up using TAB, but disable for ALT+TAB of course */ - if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 && -#endif - !_networking && _game_mode != GM_MENU) { - _fast_forward |= 2; - } else if (_fast_forward & 2) { - _fast_forward = 0; - } - - cur_ticks = GetTickCount(); - if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) { - next_tick = cur_ticks + 30; - _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; - _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; -#ifdef _DEBUG - _dbg_screen_rect = _wnd.has_focus && GetAsyncKeyState(VK_CAPITAL)<0; -#endif - - // determine which directional keys are down - if (_wnd.has_focus) { - _dirkeys = - (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) + - (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) + - (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) + - (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0); - } else { - _dirkeys = 0; - } - - GameLoop(); - _cursor.delta.x = _cursor.delta.y = 0; - - if (_force_full_redraw) MarkWholeScreenDirty(); - - GdiFlush(); - _screen.dst_ptr = _wnd.buffer_bits; - UpdateWindows(); - CheckPaletteAnim(); - } else { - Sleep(1); - GdiFlush(); - _screen.dst_ptr = _wnd.buffer_bits; - DrawTextMessage(); - DrawMouseCursor(); - } - } -} - -static bool Win32GdiChangeRes(int w, int h) -{ - _wnd.width = _wnd.width_org = w; - _wnd.height = _wnd.height_org = h; - - MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching - - return true; -} - -static void Win32GdiFullScreen(bool full_screen) -{ - MakeWindow(full_screen); -} - -const HalVideoDriver _win32_video_driver = { - Win32GdiStart, - Win32GdiStop, - Win32GdiMakeDirty, - Win32GdiMainLoop, - Win32GdiChangeRes, - Win32GdiFullScreen, -}; diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp new file mode 100644 index 000000000..eab832ae5 --- /dev/null +++ b/src/video/win32_v.cpp @@ -0,0 +1,876 @@ +/* $Id$ */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../functions.h" +#include "../gfx.h" +#include "../macros.h" +#include "../network/network.h" +#include "../variables.h" +#include "../win32.h" +#include "../window.h" +#include "win32_v.h" +#include +#include + +static struct { + HWND main_wnd; + HBITMAP dib_sect; + Pixel *bitmap_bits; + Pixel *buffer_bits; + Pixel *alloced_bits; + HPALETTE gdi_palette; + int width; + int height; + int width_org; + int height_org; + bool fullscreen; + bool double_size; + bool has_focus; + bool running; +} _wnd; + +bool _force_full_redraw; +bool _double_size; +bool _window_maximize; +uint _display_hz; +uint _fullscreen_bpp; +static uint16 _bck_resolution[2]; + +static void MakePalette(void) +{ + LOGPALETTE *pal; + uint i; + + pal = alloca(sizeof(LOGPALETTE) + (256-1) * sizeof(PALETTEENTRY)); + + pal->palVersion = 0x300; + pal->palNumEntries = 256; + + for (i = 0; i != 256; i++) { + pal->palPalEntry[i].peRed = _cur_palette[i].r; + pal->palPalEntry[i].peGreen = _cur_palette[i].g; + pal->palPalEntry[i].peBlue = _cur_palette[i].b; + pal->palPalEntry[i].peFlags = 0; + + } + _wnd.gdi_palette = CreatePalette(pal); + if (_wnd.gdi_palette == NULL) error("CreatePalette failed!\n"); +} + +static void UpdatePalette(HDC dc, uint start, uint count) +{ + RGBQUAD rgb[256]; + uint i; + + for (i = 0; i != count; i++) { + rgb[i].rgbRed = _cur_palette[start + i].r; + rgb[i].rgbGreen = _cur_palette[start + i].g; + rgb[i].rgbBlue = _cur_palette[start + i].b; + rgb[i].rgbReserved = 0; + } + + SetDIBColorTable(dc, start, count, rgb); +} + +typedef struct { + byte vk_from; + byte vk_count; + byte map_to; +} VkMapping; + +#define AS(x, z) {x, 0, z} +#define AM(x, y, z, w) {x, y - x, z} + +static const VkMapping _vk_mapping[] = { + // Pageup stuff + up/down + AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN), + // Map letters & digits + AM('A','Z','A','Z'), + AM('0','9','0','9'), + + AS(VK_ESCAPE, WKC_ESC), + AS(VK_PAUSE, WKC_PAUSE), + AS(VK_BACK, WKC_BACKSPACE), + AM(VK_INSERT, VK_DELETE, WKC_INSERT, WKC_DELETE), + + AS(VK_SPACE, WKC_SPACE), + AS(VK_RETURN, WKC_RETURN), + AS(VK_TAB, WKC_TAB), + + // Function keys + AM(VK_F1, VK_F12, WKC_F1, WKC_F12), + + // Numeric part. + // What is the virtual keycode for numeric enter?? + AM(VK_NUMPAD0, VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9), + AS(VK_DIVIDE, WKC_NUM_DIV), + AS(VK_MULTIPLY, WKC_NUM_MUL), + AS(VK_SUBTRACT, WKC_NUM_MINUS), + AS(VK_ADD, WKC_NUM_PLUS), + AS(VK_DECIMAL, WKC_NUM_DECIMAL) +}; + +static uint MapWindowsKey(uint sym) +{ + const VkMapping *map; + uint key = 0; + + for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { + if ((uint)(sym - map->vk_from) <= map->vk_count) { + key = sym - map->vk_from + map->map_to; + break; + } + } + + if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT; + if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL; + if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT; + return key; +} + +static bool AllocateDibSection(int w, int h); + +static void ClientSizeChanged(int w, int h) +{ + if (_wnd.double_size) { + w /= 2; + h /= 2; + } + + // allocate new dib section of the new size + if (AllocateDibSection(w, h)) { + // mark all palette colors dirty + _pal_first_dirty = 0; + _pal_last_dirty = 255; + GameSizeChanged(); + + // redraw screen + if (_wnd.running) { + _screen.dst_ptr = _wnd.buffer_bits; + UpdateWindows(); + } + } +} + +#ifdef _DEBUG +// Keep this function here.. +// It allows you to redraw the screen from within the MSVC debugger +int RedrawScreenDebug(void) +{ + HDC dc,dc2; + static int _fooctr; + HBITMAP old_bmp; + HPALETTE old_palette; + + _screen.dst_ptr = _wnd.buffer_bits; + UpdateWindows(); + + dc = GetDC(_wnd.main_wnd); + dc2 = CreateCompatibleDC(dc); + + old_bmp = SelectObject(dc2, _wnd.dib_sect); + old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); + BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); + SelectPalette(dc, old_palette, TRUE); + SelectObject(dc2, old_bmp); + DeleteDC(dc2); + ReleaseDC(_wnd.main_wnd, dc); + + return _fooctr++; +} +#endif + +/* Windows 95 will not have a WM_MOUSELEAVE message, so define it if needed */ +#if !defined(WM_MOUSELEAVE) +#define WM_MOUSELEAVE 0x02A3 +#endif +#define TID_POLLMOUSE 1 +#define MOUSE_POLL_DELAY 75 + +static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time) +{ + RECT rc; + POINT pt; + + /* Get the rectangle of our window and translate it to screen coordinates. + * Compare this with the current screen coordinates of the mouse and if it + * falls outside of the area or our window we have left the window. */ + GetClientRect(hwnd, &rc); + MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2); + GetCursorPos(&pt); + + if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) { + KillTimer(hwnd, event); + PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L); + } +} + +static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_CREATE: + SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); + break; + + case WM_PAINT: { + PAINTSTRUCT ps; + HDC dc,dc2; + HBITMAP old_bmp; + HPALETTE old_palette; + + BeginPaint(hwnd, &ps); + dc = ps.hdc; + dc2 = CreateCompatibleDC(dc); + old_bmp = SelectObject(dc2, _wnd.dib_sect); + old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); + + if (_pal_last_dirty != -1) { + UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); + _pal_last_dirty = -1; + } + + BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); + SelectPalette(dc, old_palette, TRUE); + SelectObject(dc2, old_bmp); + DeleteDC(dc2); + EndPaint(hwnd, &ps); + return 0; + } + + case WM_PALETTECHANGED: + if ((HWND)wParam == hwnd) return 0; + /* FALLTHROUGH */ + + case WM_QUERYNEWPALETTE: { + HDC hDC = GetWindowDC(hwnd); + HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE); + UINT nChanged = RealizePalette(hDC); + + SelectPalette(hDC, hOldPalette, TRUE); + ReleaseDC(hwnd, hDC); + if (nChanged) InvalidateRect(hwnd, NULL, FALSE); + return 0; + } + + case WM_CLOSE: + HandleExitGameRequest(); + return 0; + + case WM_DESTROY: + if (_window_maximize) { + _cur_resolution[0] = _bck_resolution[0]; + _cur_resolution[1] = _bck_resolution[1]; + } + return 0; + + case WM_LBUTTONDOWN: + SetCapture(hwnd); + _left_button_down = true; + HandleMouseEvents(); + return 0; + + case WM_LBUTTONUP: + ReleaseCapture(); + _left_button_down = false; + _left_button_clicked = false; + HandleMouseEvents(); + return 0; + + case WM_RBUTTONDOWN: + SetCapture(hwnd); + _right_button_down = true; + _right_button_clicked = true; + HandleMouseEvents(); + return 0; + + case WM_RBUTTONUP: + ReleaseCapture(); + _right_button_down = false; + HandleMouseEvents(); + return 0; + + case WM_MOUSELEAVE: + UndrawMouseCursor(); + _cursor.in_window = false; + + if (!_left_button_down && !_right_button_down) MyShowCursor(true); + HandleMouseEvents(); + return 0; + + case WM_MOUSEMOVE: { + int x = (int16)LOWORD(lParam); + int y = (int16)HIWORD(lParam); + POINT pt; + + /* If the mouse was not in the window and it has moved it means it has + * come into the window, so start drawing the mouse. Also start + * tracking the mouse for exiting the window */ + if (!_cursor.in_window) { + _cursor.in_window = true; + SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); + + DrawMouseCursor(); + } + + if (_wnd.double_size) { + x /= 2; + y /= 2; + } + + if (_cursor.fix_at) { + int dx = x - _cursor.pos.x; + int dy = y - _cursor.pos.y; + if (dx != 0 || dy != 0) { + _cursor.delta.x += dx; + _cursor.delta.y += dy; + + pt.x = _cursor.pos.x; + pt.y = _cursor.pos.y; + + if (_wnd.double_size) { + pt.x *= 2; + pt.y *= 2; + } + ClientToScreen(hwnd, &pt); + SetCursorPos(pt.x, pt.y); + } + } else { + _cursor.delta.x += x - _cursor.pos.x; + _cursor.delta.y += y - _cursor.pos.y; + _cursor.pos.x = x; + _cursor.pos.y = y; + _cursor.dirty = true; + } + MyShowCursor(false); + HandleMouseEvents(); + return 0; + } + + case WM_KEYDOWN: { + // this is the rewritten ascii input function + // it disables windows deadkey handling --> more linux like :D + wchar_t w = 0; + byte ks[256]; + uint scancode; + uint32 pressed_key; + + GetKeyboardState(ks); + if (ToUnicode(wParam, 0, ks, &w, 1, 0) != 1) { + /* On win9x ToUnicode always fails, so fall back to ToAscii */ + if (ToAscii(wParam, 0, ks, &w, 0) != 1) w = 0; // no translation was possible + } + + pressed_key = w | MapWindowsKey(wParam) << 16; + + scancode = GB(lParam, 16, 8); + if (scancode == 41) pressed_key = w | WKC_BACKQUOTE << 16; + + if (GB(pressed_key, 16, 16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) { + _double_size ^= 1; + _wnd.double_size = _double_size; + ClientSizeChanged(_wnd.width, _wnd.height); + MarkWholeScreenDirty(); + } + HandleKeypress(pressed_key); + break; + } + + case WM_SYSKEYDOWN: /* user presses F10 or Alt, both activating the title-menu */ + switch (wParam) { + case VK_RETURN: + case 'F': /* Full Screen on ALT + ENTER/F */ + ToggleFullScreen(!_wnd.fullscreen); + return 0; + + case VK_MENU: /* Just ALT */ + return 0; // do nothing + + case VK_F10: /* F10, ignore activation of menu */ + HandleKeypress(MapWindowsKey(wParam) << 16); + return 0; + + default: /* ALT in combination with something else */ + HandleKeypress(MapWindowsKey(wParam) << 16); + break; + } + break; + + case WM_SIZE: + if (wParam != SIZE_MINIMIZED) { + /* Set maximized flag when we maximize (obviously), but also when we + * switched to fullscreen from a maximized state */ + _window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen)); + if (_window_maximize) { + _bck_resolution[0] = _cur_resolution[0]; + _bck_resolution[1] = _cur_resolution[1]; + } + ClientSizeChanged(LOWORD(lParam), HIWORD(lParam)); + } + return 0; + + case WM_SIZING: { + RECT* r = (RECT*)lParam; + RECT r2; + int w, h; + + SetRect(&r2, 0, 0, 0, 0); + AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); + + w = r->right - r->left - (r2.right - r2.left); + h = r->bottom - r->top - (r2.bottom - r2.top); + if (_wnd.double_size) { + w /= 2; + h /= 2; + } + w = clamp(w, 64, MAX_SCREEN_WIDTH); + h = clamp(h, 64, MAX_SCREEN_HEIGHT); + if (_wnd.double_size) { + w *= 2; + h *= 2; + } + SetRect(&r2, 0, 0, w, h); + + AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); + w = r2.right - r2.left; + h = r2.bottom - r2.top; + + switch (wParam) { + case WMSZ_BOTTOM: + r->bottom = r->top + h; + break; + + case WMSZ_BOTTOMLEFT: + r->bottom = r->top + h; + r->left = r->right - w; + break; + + case WMSZ_BOTTOMRIGHT: + r->bottom = r->top + h; + r->right = r->left + w; + break; + + case WMSZ_LEFT: + r->left = r->right - w; + break; + + case WMSZ_RIGHT: + r->right = r->left + w; + break; + + case WMSZ_TOP: + r->top = r->bottom - h; + break; + + case WMSZ_TOPLEFT: + r->top = r->bottom - h; + r->left = r->right - w; + break; + + case WMSZ_TOPRIGHT: + r->top = r->bottom - h; + r->right = r->left + w; + break; + } + return TRUE; + } + +// needed for wheel +#if !defined(WM_MOUSEWHEEL) +# define WM_MOUSEWHEEL 0x020A +#endif //WM_MOUSEWHEEL +#if !defined(GET_WHEEL_DELTA_WPARAM) +# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam)) +#endif //GET_WHEEL_DELTA_WPARAM + + case WM_MOUSEWHEEL: { + int delta = GET_WHEEL_DELTA_WPARAM(wParam); + + if (delta < 0) { + _cursor.wheel++; + } else if (delta > 0) { + _cursor.wheel--; + } + HandleMouseEvents(); + return 0; + } + + case WM_ACTIVATEAPP: + _wnd.has_focus = (bool)wParam; + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +static void RegisterWndClass(void) +{ + static bool registered = false; + + if (!registered) { + HINSTANCE hinst = GetModuleHandle(NULL); + WNDCLASS wnd = { + 0, + WndProcGdi, + 0, + 0, + hinst, + LoadIcon(hinst, MAKEINTRESOURCE(100)), + LoadCursor(NULL, IDC_ARROW), + 0, + 0, + _T("OTTD") + }; + + registered = true; + if (!RegisterClass(&wnd)) error("RegisterClass failed"); + } +} + +static void MakeWindow(bool full_screen) +{ + _fullscreen = full_screen; + + _wnd.double_size = _double_size && !full_screen; + + // recreate window? + if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) { + DestroyWindow(_wnd.main_wnd); + _wnd.main_wnd = 0; + } + + if (full_screen) { + DEVMODE settings; + + memset(&settings, 0, sizeof(settings)); + settings.dmSize = sizeof(settings); + settings.dmFields = + (_fullscreen_bpp != 0 ? DM_BITSPERPEL : 0) | + DM_PELSWIDTH | + DM_PELSHEIGHT | + (_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0); + settings.dmBitsPerPel = _fullscreen_bpp; + settings.dmPelsWidth = _wnd.width_org; + settings.dmPelsHeight = _wnd.height_org; + settings.dmDisplayFrequency = _display_hz; + + if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { + MakeWindow(false); + return; + } + } else if (_wnd.fullscreen) { + // restore display? + ChangeDisplaySettings(NULL, 0); + } + + { + RECT r; + DWORD style, showstyle; + int x, y, w, h; + + showstyle = SW_SHOWNORMAL; + _wnd.fullscreen = full_screen; + if (_wnd.fullscreen) { + style = WS_POPUP; + SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org); + } else { + style = WS_OVERLAPPEDWINDOW; + /* On window creation, check if we were in maximize mode before */ + if (_window_maximize) showstyle = SW_SHOWMAXIMIZED; + SetRect(&r, 0, 0, _wnd.width, _wnd.height); + } + + AdjustWindowRect(&r, style, FALSE); + w = r.right - r.left; + h = r.bottom - r.top; + x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; + y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; + + if (_wnd.main_wnd) { + ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL); // remove maximize-flag + SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + } else { + extern const char _openttd_revision[]; + TCHAR Windowtitle[50]; + + _sntprintf(Windowtitle, sizeof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision)); + + _wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); + if (_wnd.main_wnd == NULL) error("CreateWindow failed"); + ShowWindow(_wnd.main_wnd, showstyle); + } + } + GameSizeChanged(); // invalidate all windows, force redraw +} + +static bool AllocateDibSection(int w, int h) +{ + BITMAPINFO *bi; + HDC dc; + + w = clamp(w, 64, MAX_SCREEN_WIDTH); + h = clamp(h, 64, MAX_SCREEN_HEIGHT); + + if (w == _screen.width && h == _screen.height) + return false; + + _screen.width = w; + _screen.pitch = ALIGN(w, 4); + _screen.height = h; + + if (_wnd.alloced_bits) { + free(_wnd.alloced_bits); + _wnd.alloced_bits = NULL; + } + + bi = alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); + memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); + bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + + if (_wnd.double_size) { + w = ALIGN(w, 4); + _wnd.alloced_bits = _wnd.buffer_bits = malloc(w * h); + w *= 2; + h *= 2; + } + + bi->bmiHeader.biWidth = _wnd.width = w; + bi->bmiHeader.biHeight = -(_wnd.height = h); + + bi->bmiHeader.biPlanes = 1; + bi->bmiHeader.biBitCount = 8; + bi->bmiHeader.biCompression = BI_RGB; + + if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect); + + dc = GetDC(0); + _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.bitmap_bits, NULL, 0); + if (_wnd.dib_sect == NULL) error("CreateDIBSection failed"); + ReleaseDC(0, dc); + + if (!_wnd.double_size) _wnd.buffer_bits = _wnd.bitmap_bits; + + return true; +} + +static const uint16 default_resolutions[][2] = { + { 640, 480 }, + { 800, 600 }, + { 1024, 768 }, + { 1152, 864 }, + { 1280, 800 }, + { 1280, 960 }, + { 1280, 1024 }, + { 1400, 1050 }, + { 1600, 1200 }, + { 1680, 1050 }, + { 1920, 1200 } +}; + +static void FindResolutions(void) +{ + uint n = 0; + uint i; + DEVMODEA dm; + + /* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95 + * Doesn't really matter since we don't pass a string anyways, but still + * a letdown */ + for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) { + if (dm.dmBitsPerPel == 8 && IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) && + IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)) { + uint j; + + for (j = 0; j < n; j++) { + if (_resolutions[j][0] == dm.dmPelsWidth && _resolutions[j][1] == dm.dmPelsHeight) break; + } + + /* In the previous loop we have checked already existing/added resolutions if + * they are the same as the new ones. If this is not the case (j == n); we have + * looped all and found none, add the new one to the list. If we have reached the + * maximum amount of resolutions, then quit querying the display */ + if (j == n) { + _resolutions[j][0] = dm.dmPelsWidth; + _resolutions[j][1] = dm.dmPelsHeight; + if (++n == lengthof(_resolutions)) break; + } + } + } + + /* We have found no resolutions, show the default list */ + if (n == 0) { + memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); + n = lengthof(default_resolutions); + } + + _num_resolutions = n; + SortResolutions(_num_resolutions); +} + + +static const char *Win32GdiStart(const char * const *parm) +{ + memset(&_wnd, 0, sizeof(_wnd)); + + RegisterWndClass(); + + MakePalette(); + + FindResolutions(); + + // fullscreen uses those + _wnd.width_org = _cur_resolution[0]; + _wnd.height_org = _cur_resolution[1]; + + AllocateDibSection(_cur_resolution[0], _cur_resolution[1]); + MarkWholeScreenDirty(); + + MakeWindow(_fullscreen); + + return NULL; +} + +static void Win32GdiStop(void) +{ + DeleteObject(_wnd.gdi_palette); + DeleteObject(_wnd.dib_sect); + DestroyWindow(_wnd.main_wnd); + + if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0); + if (_double_size) { + _cur_resolution[0] *= 2; + _cur_resolution[1] *= 2; + } + + MyShowCursor(true); +} + +// simple upscaler by 2 +static void filter(int left, int top, int width, int height) +{ + uint p = _screen.pitch; + const Pixel *s = _wnd.buffer_bits + top * p + left; + Pixel *d = _wnd.bitmap_bits + top * p * 4 + left * 2; + + for (; height > 0; height--) { + int i; + + for (i = 0; i != width; i++) { + d[i * 2] = d[i * 2 + 1] = d[i * 2 + p * 2] = d[i * 2 + 1 + p * 2] = s[i]; + } + s += p; + d += p * 4; + } +} + +static void Win32GdiMakeDirty(int left, int top, int width, int height) +{ + RECT r = { left, top, left + width, top + height }; + + if (_wnd.double_size) { + filter(left, top, width, height); + r.left *= 2; + r.top *= 2; + r.right *= 2; + r.bottom *= 2; + } + InvalidateRect(_wnd.main_wnd, &r, FALSE); +} + +static void CheckPaletteAnim(void) +{ + if (_pal_last_dirty == -1) + return; + InvalidateRect(_wnd.main_wnd, NULL, FALSE); +} + +static void Win32GdiMainLoop(void) +{ + MSG mesg; + uint32 cur_ticks = GetTickCount(); + uint32 next_tick = cur_ticks + 30; + + _wnd.running = true; + + for (;;) { + uint32 prev_cur_ticks = cur_ticks; // to check for wrapping + + while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) { + InteractiveRandom(); // randomness + DispatchMessage(&mesg); + } + if (_exit_game) return; + +#if defined(_DEBUG) + if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 && +#else + /* Speed up using TAB, but disable for ALT+TAB of course */ + if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 && +#endif + !_networking && _game_mode != GM_MENU) { + _fast_forward |= 2; + } else if (_fast_forward & 2) { + _fast_forward = 0; + } + + cur_ticks = GetTickCount(); + if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) { + next_tick = cur_ticks + 30; + _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; + _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; +#ifdef _DEBUG + _dbg_screen_rect = _wnd.has_focus && GetAsyncKeyState(VK_CAPITAL)<0; +#endif + + // determine which directional keys are down + if (_wnd.has_focus) { + _dirkeys = + (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) + + (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) + + (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) + + (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0); + } else { + _dirkeys = 0; + } + + GameLoop(); + _cursor.delta.x = _cursor.delta.y = 0; + + if (_force_full_redraw) MarkWholeScreenDirty(); + + GdiFlush(); + _screen.dst_ptr = _wnd.buffer_bits; + UpdateWindows(); + CheckPaletteAnim(); + } else { + Sleep(1); + GdiFlush(); + _screen.dst_ptr = _wnd.buffer_bits; + DrawTextMessage(); + DrawMouseCursor(); + } + } +} + +static bool Win32GdiChangeRes(int w, int h) +{ + _wnd.width = _wnd.width_org = w; + _wnd.height = _wnd.height_org = h; + + MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching + + return true; +} + +static void Win32GdiFullScreen(bool full_screen) +{ + MakeWindow(full_screen); +} + +const HalVideoDriver _win32_video_driver = { + Win32GdiStart, + Win32GdiStop, + Win32GdiMakeDirty, + Win32GdiMainLoop, + Win32GdiChangeRes, + Win32GdiFullScreen, +}; -- cgit v1.2.3-70-g09d2