src/video/sdl_v.cpp
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5648 1608018c5ff2
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
       
     1 /* $Id$ */
       
     2 
       
     3 #include "../stdafx.h"
       
     4 
       
     5 #ifdef WITH_SDL
       
     6 
       
     7 #include "../openttd.h"
       
     8 #include "../debug.h"
       
     9 #include "../functions.h"
       
    10 #include "../gfx.h"
       
    11 #include "../macros.h"
       
    12 #include "../sdl.h"
       
    13 #include "../window.h"
       
    14 #include "../network/network.h"
       
    15 #include "../variables.h"
       
    16 #include "sdl_v.h"
       
    17 #include <SDL.h>
       
    18 
       
    19 static SDL_Surface *_sdl_screen;
       
    20 static bool _all_modes;
       
    21 
       
    22 #define MAX_DIRTY_RECTS 100
       
    23 static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
       
    24 static int _num_dirty_rects;
       
    25 
       
    26 static void SdlVideoMakeDirty(int left, int top, int width, int height)
       
    27 {
       
    28 	if (_num_dirty_rects < MAX_DIRTY_RECTS) {
       
    29 		_dirty_rects[_num_dirty_rects].x = left;
       
    30 		_dirty_rects[_num_dirty_rects].y = top;
       
    31 		_dirty_rects[_num_dirty_rects].w = width;
       
    32 		_dirty_rects[_num_dirty_rects].h = height;
       
    33 	}
       
    34 	_num_dirty_rects++;
       
    35 }
       
    36 
       
    37 static void UpdatePalette(uint start, uint count)
       
    38 {
       
    39 	SDL_Color pal[256];
       
    40 	uint i;
       
    41 
       
    42 	for (i = 0; i != count; i++) {
       
    43 		pal[i].r = _cur_palette[start + i].r;
       
    44 		pal[i].g = _cur_palette[start + i].g;
       
    45 		pal[i].b = _cur_palette[start + i].b;
       
    46 		pal[i].unused = 0;
       
    47 	}
       
    48 
       
    49 	SDL_CALL SDL_SetColors(_sdl_screen, pal, start, count);
       
    50 }
       
    51 
       
    52 static void InitPalette(void)
       
    53 {
       
    54 	UpdatePalette(0, 256);
       
    55 }
       
    56 
       
    57 static void CheckPaletteAnim(void)
       
    58 {
       
    59 	if (_pal_last_dirty != -1) {
       
    60 		UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1);
       
    61 		_pal_last_dirty = -1;
       
    62 	}
       
    63 }
       
    64 
       
    65 static void DrawSurfaceToScreen(void)
       
    66 {
       
    67 	int n = _num_dirty_rects;
       
    68 	if (n != 0) {
       
    69 		_num_dirty_rects = 0;
       
    70 		if (n > MAX_DIRTY_RECTS)
       
    71 			SDL_CALL SDL_UpdateRect(_sdl_screen, 0, 0, 0, 0);
       
    72 		else
       
    73 			SDL_CALL SDL_UpdateRects(_sdl_screen, n, _dirty_rects);
       
    74 	}
       
    75 }
       
    76 
       
    77 static const uint16 default_resolutions[][2] = {
       
    78 	{ 640,  480},
       
    79 	{ 800,  600},
       
    80 	{1024,  768},
       
    81 	{1152,  864},
       
    82 	{1280,  800},
       
    83 	{1280,  960},
       
    84 	{1280, 1024},
       
    85 	{1400, 1050},
       
    86 	{1600, 1200},
       
    87 	{1680, 1050},
       
    88 	{1920, 1200}
       
    89 };
       
    90 
       
    91 static void GetVideoModes(void)
       
    92 {
       
    93 	int i;
       
    94 	SDL_Rect **modes;
       
    95 
       
    96 	modes = SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE + (_fullscreen ? SDL_FULLSCREEN : 0));
       
    97 
       
    98 	if (modes == NULL)
       
    99 		error("sdl: no modes available");
       
   100 
       
   101 	_all_modes = (modes == (void*)-1);
       
   102 
       
   103 	if (_all_modes) {
       
   104 		// all modes available, put some default ones here
       
   105 		memcpy(_resolutions, default_resolutions, sizeof(default_resolutions));
       
   106 		_num_resolutions = lengthof(default_resolutions);
       
   107 	} else {
       
   108 		int n = 0;
       
   109 		for (i = 0; modes[i]; i++) {
       
   110 			int w = modes[i]->w;
       
   111 			int h = modes[i]->h;
       
   112 			if (IS_INT_INSIDE(w, 640, MAX_SCREEN_WIDTH + 1) &&
       
   113 					IS_INT_INSIDE(h, 480, MAX_SCREEN_HEIGHT + 1)) {
       
   114 				int j;
       
   115 				for (j = 0; j < n; j++) {
       
   116 					if (_resolutions[j][0] == w && _resolutions[j][1] == h) break;
       
   117 				}
       
   118 
       
   119 				if (j == n) {
       
   120 					_resolutions[j][0] = w;
       
   121 					_resolutions[j][1] = h;
       
   122 					if (++n == lengthof(_resolutions)) break;
       
   123 				}
       
   124 			}
       
   125 		}
       
   126 		_num_resolutions = n;
       
   127 		SortResolutions(_num_resolutions);
       
   128 	}
       
   129 }
       
   130 
       
   131 static void GetAvailableVideoMode(int *w, int *h)
       
   132 {
       
   133 	int i;
       
   134 	int best;
       
   135 	uint delta;
       
   136 
       
   137 	// all modes available?
       
   138 	if (_all_modes) return;
       
   139 
       
   140 	// is the wanted mode among the available modes?
       
   141 	for (i = 0; i != _num_resolutions; i++) {
       
   142 		if (*w == _resolutions[i][0] && *h == _resolutions[i][1]) return;
       
   143 	}
       
   144 
       
   145 	// use the closest possible resolution
       
   146 	best = 0;
       
   147 	delta = abs((_resolutions[0][0] - *w) * (_resolutions[0][1] - *h));
       
   148 	for (i = 1; i != _num_resolutions; ++i) {
       
   149 		uint newdelta = abs((_resolutions[i][0] - *w) * (_resolutions[i][1] - *h));
       
   150 		if (newdelta < delta) {
       
   151 			best = i;
       
   152 			delta = newdelta;
       
   153 		}
       
   154 	}
       
   155 	*w = _resolutions[best][0];
       
   156 	*h = _resolutions[best][1];
       
   157 }
       
   158 
       
   159 #ifndef ICON_DIR
       
   160 #define ICON_DIR "media"
       
   161 #endif
       
   162 
       
   163 #ifdef WIN32
       
   164 /* Let's redefine the LoadBMP macro with because we are dynamically
       
   165  * loading SDL and need to 'SDL_CALL' all functions */
       
   166 #undef SDL_LoadBMP
       
   167 #define SDL_LoadBMP(file)	SDL_LoadBMP_RW(SDL_CALL SDL_RWFromFile(file, "rb"), 1)
       
   168 #endif
       
   169 
       
   170 static bool CreateMainSurface(int w, int h)
       
   171 {
       
   172 	extern const char _openttd_revision[];
       
   173 	SDL_Surface *newscreen, *icon;
       
   174 	char caption[50];
       
   175 
       
   176 	GetAvailableVideoMode(&w, &h);
       
   177 
       
   178 	DEBUG(driver, 1, "SDL: using mode %dx%d", w, h);
       
   179 
       
   180 	/* Give the application an icon */
       
   181 	icon = SDL_CALL SDL_LoadBMP(ICON_DIR PATHSEP "openttd.32.bmp");
       
   182 	if (icon != NULL) {
       
   183 		/* Get the colourkey, which will be magenta */
       
   184 		uint32 rgbmap = SDL_CALL SDL_MapRGB(icon->format, 255, 0, 255);
       
   185 
       
   186 		SDL_CALL SDL_SetColorKey(icon, SDL_SRCCOLORKEY, rgbmap);
       
   187 		SDL_CALL SDL_WM_SetIcon(icon, NULL);
       
   188 		SDL_CALL SDL_FreeSurface(icon);
       
   189 	}
       
   190 
       
   191 	// DO NOT CHANGE TO HWSURFACE, IT DOES NOT WORK
       
   192 	newscreen = SDL_CALL SDL_SetVideoMode(w, h, 8, SDL_SWSURFACE | SDL_HWPALETTE | (_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE));
       
   193 	if (newscreen == NULL)
       
   194 		return false;
       
   195 
       
   196 	_screen.width = newscreen->w;
       
   197 	_screen.height = newscreen->h;
       
   198 	_screen.pitch = newscreen->pitch;
       
   199 
       
   200 	_sdl_screen = newscreen;
       
   201 	InitPalette();
       
   202 
       
   203 	snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
       
   204 	SDL_CALL SDL_WM_SetCaption(caption, caption);
       
   205 	SDL_CALL SDL_ShowCursor(0);
       
   206 
       
   207 	GameSizeChanged();
       
   208 
       
   209 	return true;
       
   210 }
       
   211 
       
   212 typedef struct VkMapping {
       
   213 	uint16 vk_from;
       
   214 	byte vk_count;
       
   215 	byte map_to;
       
   216 } VkMapping;
       
   217 
       
   218 #define AS(x, z) {x, 0, z}
       
   219 #define AM(x, y, z, w) {x, y - x, z}
       
   220 
       
   221 static const VkMapping _vk_mapping[] = {
       
   222 	// Pageup stuff + up/down
       
   223 	AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),
       
   224 	AS(SDLK_UP,     WKC_UP),
       
   225 	AS(SDLK_DOWN,   WKC_DOWN),
       
   226 	AS(SDLK_LEFT,   WKC_LEFT),
       
   227 	AS(SDLK_RIGHT,  WKC_RIGHT),
       
   228 
       
   229 	AS(SDLK_HOME,   WKC_HOME),
       
   230 	AS(SDLK_END,    WKC_END),
       
   231 
       
   232 	AS(SDLK_INSERT, WKC_INSERT),
       
   233 	AS(SDLK_DELETE, WKC_DELETE),
       
   234 
       
   235 	// Map letters & digits
       
   236 	AM(SDLK_a, SDLK_z, 'A', 'Z'),
       
   237 	AM(SDLK_0, SDLK_9, '0', '9'),
       
   238 
       
   239 	AS(SDLK_ESCAPE,    WKC_ESC),
       
   240 	AS(SDLK_PAUSE,     WKC_PAUSE),
       
   241 	AS(SDLK_BACKSPACE, WKC_BACKSPACE),
       
   242 
       
   243 	AS(SDLK_SPACE,     WKC_SPACE),
       
   244 	AS(SDLK_RETURN,    WKC_RETURN),
       
   245 	AS(SDLK_TAB,       WKC_TAB),
       
   246 
       
   247 	// Function keys
       
   248 	AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
       
   249 
       
   250 	// Numeric part.
       
   251 	// What is the virtual keycode for numeric enter??
       
   252 	AM(SDLK_KP0, SDLK_KP9, WKC_NUM_0, WKC_NUM_9),
       
   253 	AS(SDLK_KP_DIVIDE,   WKC_NUM_DIV),
       
   254 	AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
       
   255 	AS(SDLK_KP_MINUS,    WKC_NUM_MINUS),
       
   256 	AS(SDLK_KP_PLUS,     WKC_NUM_PLUS),
       
   257 	AS(SDLK_KP_ENTER,    WKC_NUM_ENTER),
       
   258 	AS(SDLK_KP_PERIOD,   WKC_NUM_DECIMAL)
       
   259 };
       
   260 
       
   261 static uint32 ConvertSdlKeyIntoMy(SDL_keysym *sym)
       
   262 {
       
   263 	const VkMapping *map;
       
   264 	uint key = 0;
       
   265 
       
   266 	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
       
   267 		if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
       
   268 			key = sym->sym - map->vk_from + map->map_to;
       
   269 			break;
       
   270 		}
       
   271 	}
       
   272 
       
   273 	// check scancode for BACKQUOTE key, because we want the key left of "1", not anything else (on non-US keyboards)
       
   274 #if defined(WIN32) || defined(__OS2__)
       
   275 	if (sym->scancode == 41) key = WKC_BACKQUOTE;
       
   276 #elif defined(__APPLE__)
       
   277 	if (sym->scancode == 10) key = WKC_BACKQUOTE;
       
   278 #elif defined(__MORPHOS__)
       
   279 	if (sym->scancode == 0)  key = WKC_BACKQUOTE;  // yes, that key is code '0' under MorphOS :)
       
   280 #elif defined(__BEOS__)
       
   281 	if (sym->scancode == 17) key = WKC_BACKQUOTE;
       
   282 #elif defined(__SVR4) && defined(__sun)
       
   283 	if (sym->scancode == 60) key = WKC_BACKQUOTE;
       
   284 	if (sym->scancode == 49) key = WKC_BACKSPACE;
       
   285 #elif defined(__sgi__)
       
   286 	if (sym->scancode == 22) key = WKC_BACKQUOTE;
       
   287 #else
       
   288 	if (sym->scancode == 49) key = WKC_BACKQUOTE;
       
   289 #endif
       
   290 
       
   291 	// META are the command keys on mac
       
   292 	if (sym->mod & KMOD_META)  key |= WKC_META;
       
   293 	if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
       
   294 	if (sym->mod & KMOD_CTRL)  key |= WKC_CTRL;
       
   295 	if (sym->mod & KMOD_ALT)   key |= WKC_ALT;
       
   296 	// these two lines really help porting hotkey combos. Uncomment to use -- Bjarni
       
   297 #if 0
       
   298 	DEBUG(driver, 0, "Scancode character pressed %u", sym->scancode);
       
   299 	DEBUG(driver, 0, "Unicode character pressed %u", sym->unicode);
       
   300 #endif
       
   301 	return (key << 16) + sym->unicode;
       
   302 }
       
   303 
       
   304 static int PollEvent(void)
       
   305 {
       
   306 	SDL_Event ev;
       
   307 
       
   308 	if (!SDL_CALL SDL_PollEvent(&ev)) return -2;
       
   309 
       
   310 	switch (ev.type) {
       
   311 		case SDL_MOUSEMOTION:
       
   312 			if (_cursor.fix_at) {
       
   313 				int dx = ev.motion.x - _cursor.pos.x;
       
   314 				int dy = ev.motion.y - _cursor.pos.y;
       
   315 				if (dx != 0 || dy != 0) {
       
   316 					_cursor.delta.x += dx;
       
   317 					_cursor.delta.y += dy;
       
   318 					SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
       
   319 				}
       
   320 			} else {
       
   321 				_cursor.delta.x = ev.motion.x - _cursor.pos.x;
       
   322 				_cursor.delta.y = ev.motion.y - _cursor.pos.y;
       
   323 				_cursor.pos.x = ev.motion.x;
       
   324 				_cursor.pos.y = ev.motion.y;
       
   325 				_cursor.dirty = true;
       
   326 			}
       
   327 			HandleMouseEvents();
       
   328 			break;
       
   329 
       
   330 		case SDL_MOUSEBUTTONDOWN:
       
   331 			if (_rightclick_emulate && SDL_CALL SDL_GetModState() & KMOD_CTRL) {
       
   332 				ev.button.button = SDL_BUTTON_RIGHT;
       
   333 			}
       
   334 
       
   335 			switch (ev.button.button) {
       
   336 				case SDL_BUTTON_LEFT:
       
   337 					_left_button_down = true;
       
   338 					break;
       
   339 
       
   340 				case SDL_BUTTON_RIGHT:
       
   341 					_right_button_down = true;
       
   342 					_right_button_clicked = true;
       
   343 					break;
       
   344 
       
   345 				case SDL_BUTTON_WHEELUP:   _cursor.wheel--; break;
       
   346 				case SDL_BUTTON_WHEELDOWN: _cursor.wheel++; break;
       
   347 
       
   348 				default: break;
       
   349 			}
       
   350 			HandleMouseEvents();
       
   351 			break;
       
   352 
       
   353 		case SDL_MOUSEBUTTONUP:
       
   354 			if (_rightclick_emulate) {
       
   355 				_right_button_down = false;
       
   356 				_left_button_down = false;
       
   357 				_left_button_clicked = false;
       
   358 			} else if (ev.button.button == SDL_BUTTON_LEFT) {
       
   359 				_left_button_down = false;
       
   360 				_left_button_clicked = false;
       
   361 			} else if (ev.button.button == SDL_BUTTON_RIGHT) {
       
   362 				_right_button_down = false;
       
   363 			}
       
   364 			HandleMouseEvents();
       
   365 			break;
       
   366 
       
   367 		case SDL_ACTIVEEVENT:
       
   368 			if (!(ev.active.state & SDL_APPMOUSEFOCUS)) break;
       
   369 
       
   370 			if (ev.active.gain) { // mouse entered the window, enable cursor
       
   371 				_cursor.in_window = true;
       
   372 			} else {
       
   373 				UndrawMouseCursor(); // mouse left the window, undraw cursor
       
   374 				_cursor.in_window = false;
       
   375 			}
       
   376 			break;
       
   377 
       
   378 		case SDL_QUIT: HandleExitGameRequest(); break;
       
   379 
       
   380 		case SDL_KEYDOWN: /* Toggle full-screen on ALT + ENTER/F */
       
   381 			if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_META)) &&
       
   382 					(ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
       
   383 				ToggleFullScreen(!_fullscreen);
       
   384 			} else {
       
   385 				HandleKeypress(ConvertSdlKeyIntoMy(&ev.key.keysym));
       
   386 			}
       
   387 			break;
       
   388 
       
   389 		case SDL_VIDEORESIZE: {
       
   390 			int w = clamp(ev.resize.w, 64, MAX_SCREEN_WIDTH);
       
   391 			int h = clamp(ev.resize.h, 64, MAX_SCREEN_HEIGHT);
       
   392 			ChangeResInGame(w, h);
       
   393 			break;
       
   394 		}
       
   395 	}
       
   396 	return -1;
       
   397 }
       
   398 
       
   399 static const char *SdlVideoStart(const char * const *parm)
       
   400 {
       
   401 	char buf[30];
       
   402 
       
   403 	const char *s = SdlOpen(SDL_INIT_VIDEO);
       
   404 	if (s != NULL) return s;
       
   405 
       
   406 	SDL_CALL SDL_VideoDriverName(buf, 30);
       
   407 	DEBUG(driver, 1, "SDL: using driver '%s'", buf);
       
   408 
       
   409 	GetVideoModes();
       
   410 	CreateMainSurface(_cur_resolution[0], _cur_resolution[1]);
       
   411 	MarkWholeScreenDirty();
       
   412 
       
   413 	SDL_CALL SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
       
   414 	SDL_CALL SDL_EnableUNICODE(1);
       
   415 	return NULL;
       
   416 }
       
   417 
       
   418 static void SdlVideoStop(void)
       
   419 {
       
   420 	SdlClose(SDL_INIT_VIDEO);
       
   421 }
       
   422 
       
   423 static void SdlVideoMainLoop(void)
       
   424 {
       
   425 	uint32 cur_ticks = SDL_CALL SDL_GetTicks();
       
   426 	uint32 next_tick = cur_ticks + 30;
       
   427 	uint32 pal_tick = 0;
       
   428 	uint32 mod;
       
   429 	int numkeys;
       
   430 	Uint8 *keys;
       
   431 
       
   432 	for (;;) {
       
   433 		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
       
   434 		InteractiveRandom(); // randomness
       
   435 
       
   436 		while (PollEvent() == -1) {}
       
   437 		if (_exit_game) return;
       
   438 
       
   439 		mod = SDL_CALL SDL_GetModState();
       
   440 		keys = SDL_CALL SDL_GetKeyState(&numkeys);
       
   441 #if defined(_DEBUG)
       
   442 		if (_shift_pressed)
       
   443 #else
       
   444 		/* Speedup when pressing tab, except when using ALT+TAB
       
   445 		 * to switch to another application */
       
   446 		if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
       
   447 #endif
       
   448 		{
       
   449 			if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
       
   450 		} else if (_fast_forward & 2) {
       
   451 			_fast_forward = 0;
       
   452 		}
       
   453 
       
   454 		cur_ticks = SDL_CALL SDL_GetTicks();
       
   455 		if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) {
       
   456 			next_tick = cur_ticks + 30;
       
   457 
       
   458 			_ctrl_pressed  = !!(mod & KMOD_CTRL);
       
   459 			_shift_pressed = !!(mod & KMOD_SHIFT);
       
   460 #ifdef _DEBUG
       
   461 			_dbg_screen_rect = !!(mod & KMOD_CAPS);
       
   462 #endif
       
   463 
       
   464 			// determine which directional keys are down
       
   465 			_dirkeys =
       
   466 				(keys[SDLK_LEFT]  ? 1 : 0) |
       
   467 				(keys[SDLK_UP]    ? 2 : 0) |
       
   468 				(keys[SDLK_RIGHT] ? 4 : 0) |
       
   469 				(keys[SDLK_DOWN]  ? 8 : 0);
       
   470 			GameLoop();
       
   471 
       
   472 			_screen.dst_ptr = _sdl_screen->pixels;
       
   473 			UpdateWindows();
       
   474 			if (++pal_tick > 4) {
       
   475 				CheckPaletteAnim();
       
   476 				pal_tick = 1;
       
   477 			}
       
   478 			DrawSurfaceToScreen();
       
   479 		} else {
       
   480 			SDL_CALL SDL_Delay(1);
       
   481 			_screen.dst_ptr = _sdl_screen->pixels;
       
   482 			DrawTextMessage();
       
   483 			DrawMouseCursor();
       
   484 			DrawSurfaceToScreen();
       
   485 		}
       
   486 	}
       
   487 }
       
   488 
       
   489 static bool SdlVideoChangeRes(int w, int h)
       
   490 {
       
   491 	return CreateMainSurface(w, h);
       
   492 }
       
   493 
       
   494 static void SdlVideoFullScreen(bool full_screen)
       
   495 {
       
   496 	_fullscreen = full_screen;
       
   497 	GetVideoModes(); // get the list of available video modes
       
   498 	if (_num_resolutions == 0 || !_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1])) {
       
   499 		// switching resolution failed, put back full_screen to original status
       
   500 		_fullscreen ^= true;
       
   501 	}
       
   502 }
       
   503 
       
   504 const HalVideoDriver _sdl_video_driver = {
       
   505 	SdlVideoStart,
       
   506 	SdlVideoStop,
       
   507 	SdlVideoMakeDirty,
       
   508 	SdlVideoMainLoop,
       
   509 	SdlVideoChangeRes,
       
   510 	SdlVideoFullScreen,
       
   511 };
       
   512 
       
   513 #endif