00001
00002
00005 #ifdef WITH_SDL
00006
00007 #include "../stdafx.h"
00008 #include "../openttd.h"
00009 #include "../gfx_func.h"
00010 #include "../sdl.h"
00011 #include "../variables.h"
00012 #include "../rev.h"
00013 #include "../blitter/factory.hpp"
00014 #include "../network/network.h"
00015 #include "../functions.h"
00016 #include "sdl_v.h"
00017 #include <SDL.h>
00018
00019 static FVideoDriver_SDL iFVideoDriver_SDL;
00020
00021 static SDL_Surface *_sdl_screen;
00022 static bool _all_modes;
00023
00024 #define MAX_DIRTY_RECTS 100
00025 static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
00026 static int _num_dirty_rects;
00027
00028 void VideoDriver_SDL::MakeDirty(int left, int top, int width, int height)
00029 {
00030 if (_num_dirty_rects < MAX_DIRTY_RECTS) {
00031 _dirty_rects[_num_dirty_rects].x = left;
00032 _dirty_rects[_num_dirty_rects].y = top;
00033 _dirty_rects[_num_dirty_rects].w = width;
00034 _dirty_rects[_num_dirty_rects].h = height;
00035 }
00036 _num_dirty_rects++;
00037 }
00038
00039 static void UpdatePalette(uint start, uint count)
00040 {
00041 SDL_Color pal[256];
00042
00043 for (uint i = 0; i != count; i++) {
00044 pal[i].r = _cur_palette[start + i].r;
00045 pal[i].g = _cur_palette[start + i].g;
00046 pal[i].b = _cur_palette[start + i].b;
00047 pal[i].unused = 0;
00048 }
00049
00050 SDL_CALL SDL_SetColors(_sdl_screen, pal, start, count);
00051 }
00052
00053 static void InitPalette()
00054 {
00055 UpdatePalette(0, 256);
00056 }
00057
00058 static void CheckPaletteAnim()
00059 {
00060 if (_pal_count_dirty != 0) {
00061 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00062
00063 switch (blitter->UsePaletteAnimation()) {
00064 case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
00065 UpdatePalette(_pal_first_dirty, _pal_count_dirty);
00066 break;
00067
00068 case Blitter::PALETTE_ANIMATION_BLITTER:
00069 blitter->PaletteAnimate(_pal_first_dirty, _pal_count_dirty);
00070 break;
00071
00072 case Blitter::PALETTE_ANIMATION_NONE:
00073 break;
00074
00075 default:
00076 NOT_REACHED();
00077 }
00078 _pal_count_dirty = 0;
00079 }
00080 }
00081
00082 static void DrawSurfaceToScreen()
00083 {
00084 int n = _num_dirty_rects;
00085 if (n == 0) return;
00086
00087 _num_dirty_rects = 0;
00088 if (n > MAX_DIRTY_RECTS) {
00089 SDL_CALL SDL_UpdateRect(_sdl_screen, 0, 0, 0, 0);
00090 } else {
00091 SDL_CALL SDL_UpdateRects(_sdl_screen, n, _dirty_rects);
00092 }
00093 }
00094
00095 static const Dimension _default_resolutions[] = {
00096 { 640, 480},
00097 { 800, 600},
00098 {1024, 768},
00099 {1152, 864},
00100 {1280, 800},
00101 {1280, 960},
00102 {1280, 1024},
00103 {1400, 1050},
00104 {1600, 1200},
00105 {1680, 1050},
00106 {1920, 1200}
00107 };
00108
00109 static void GetVideoModes()
00110 {
00111 SDL_Rect **modes = SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE | SDL_FULLSCREEN);
00112 if (modes == NULL) usererror("sdl: no modes available");
00113
00114 _all_modes = (SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE | (_fullscreen ? SDL_FULLSCREEN : 0)) == (void*)-1);
00115 if (modes == (void*)-1) {
00116 int n = 0;
00117 for (uint i = 0; i < lengthof(_default_resolutions); i++) {
00118 if (SDL_CALL SDL_VideoModeOK(_default_resolutions[i].width, _default_resolutions[i].height, 8, SDL_FULLSCREEN) != 0) {
00119 _resolutions[n] = _default_resolutions[i];
00120 if (++n == lengthof(_resolutions)) break;
00121 }
00122 }
00123 _num_resolutions = n;
00124 } else {
00125 int n = 0;
00126 for (int i = 0; modes[i]; i++) {
00127 int w = modes[i]->w;
00128 int h = modes[i]->h;
00129 int j;
00130 for (j = 0; j < n; j++) {
00131 if (_resolutions[j].width == w && _resolutions[j].height == h) break;
00132 }
00133
00134 if (j == n) {
00135 _resolutions[j].width = w;
00136 _resolutions[j].height = h;
00137 if (++n == lengthof(_resolutions)) break;
00138 }
00139 }
00140 _num_resolutions = n;
00141 SortResolutions(_num_resolutions);
00142 }
00143 }
00144
00145 static void GetAvailableVideoMode(int *w, int *h)
00146 {
00147
00148 if (_all_modes || _num_resolutions == 0) return;
00149
00150
00151 for (int i = 0; i != _num_resolutions; i++) {
00152 if (*w == _resolutions[i].width && *h == _resolutions[i].height) return;
00153 }
00154
00155
00156 int best = 0;
00157 uint delta = abs((_resolutions[0].width - *w) * (_resolutions[0].height - *h));
00158 for (int i = 1; i != _num_resolutions; ++i) {
00159 uint newdelta = abs((_resolutions[i].width - *w) * (_resolutions[i].height - *h));
00160 if (newdelta < delta) {
00161 best = i;
00162 delta = newdelta;
00163 }
00164 }
00165 *w = _resolutions[best].width;
00166 *h = _resolutions[best].height;
00167 }
00168
00169 #ifndef ICON_DIR
00170 #define ICON_DIR "media"
00171 #endif
00172
00173 #ifdef WIN32
00174
00175
00176 #undef SDL_LoadBMP
00177 #define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_CALL SDL_RWFromFile(file, "rb"), 1)
00178 #endif
00179
00180 static bool CreateMainSurface(int w, int h)
00181 {
00182 SDL_Surface *newscreen, *icon;
00183 char caption[50];
00184 int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth();
00185
00186 GetAvailableVideoMode(&w, &h);
00187
00188 DEBUG(driver, 1, "SDL: using mode %dx%dx%d", w, h, bpp);
00189
00190 if (bpp == 0) usererror("Can't use a blitter that blits 0 bpp for normal visuals");
00191
00192
00193 icon = SDL_CALL SDL_LoadBMP(ICON_DIR PATHSEP "openttd.32.bmp");
00194 if (icon != NULL) {
00195
00196 uint32 rgbmap = SDL_CALL SDL_MapRGB(icon->format, 255, 0, 255);
00197
00198 SDL_CALL SDL_SetColorKey(icon, SDL_SRCCOLORKEY, rgbmap);
00199 SDL_CALL SDL_WM_SetIcon(icon, NULL);
00200 SDL_CALL SDL_FreeSurface(icon);
00201 }
00202
00203
00204 newscreen = SDL_CALL SDL_SetVideoMode(w, h, bpp, SDL_SWSURFACE | SDL_HWPALETTE | (_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE));
00205 if (newscreen == NULL) {
00206 DEBUG(driver, 0, "SDL: Couldn't allocate a window to draw on");
00207 return false;
00208 }
00209
00210 _screen.width = newscreen->w;
00211 _screen.height = newscreen->h;
00212 _screen.pitch = newscreen->pitch / (bpp / 8);
00213 _sdl_screen = newscreen;
00214 InitPalette();
00215
00216 snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
00217 SDL_CALL SDL_WM_SetCaption(caption, caption);
00218 SDL_CALL SDL_ShowCursor(0);
00219
00220 GameSizeChanged();
00221
00222 return true;
00223 }
00224
00225 struct VkMapping {
00226 uint16 vk_from;
00227 byte vk_count;
00228 byte map_to;
00229 };
00230
00231 #define AS(x, z) {x, 0, z}
00232 #define AM(x, y, z, w) {x, y - x, z}
00233
00234 static const VkMapping _vk_mapping[] = {
00235
00236 AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),
00237 AS(SDLK_UP, WKC_UP),
00238 AS(SDLK_DOWN, WKC_DOWN),
00239 AS(SDLK_LEFT, WKC_LEFT),
00240 AS(SDLK_RIGHT, WKC_RIGHT),
00241
00242 AS(SDLK_HOME, WKC_HOME),
00243 AS(SDLK_END, WKC_END),
00244
00245 AS(SDLK_INSERT, WKC_INSERT),
00246 AS(SDLK_DELETE, WKC_DELETE),
00247
00248
00249 AM(SDLK_a, SDLK_z, 'A', 'Z'),
00250 AM(SDLK_0, SDLK_9, '0', '9'),
00251
00252 AS(SDLK_ESCAPE, WKC_ESC),
00253 AS(SDLK_PAUSE, WKC_PAUSE),
00254 AS(SDLK_BACKSPACE, WKC_BACKSPACE),
00255
00256 AS(SDLK_SPACE, WKC_SPACE),
00257 AS(SDLK_RETURN, WKC_RETURN),
00258 AS(SDLK_TAB, WKC_TAB),
00259
00260
00261 AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
00262
00263
00264 AM(SDLK_KP0, SDLK_KP9, '0', '9'),
00265 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
00266 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
00267 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
00268 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
00269 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
00270 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
00271
00272
00273 AS(SDLK_SLASH, WKC_SLASH),
00274 AS(SDLK_SEMICOLON, WKC_SEMICOLON),
00275 AS(SDLK_EQUALS, WKC_EQUALS),
00276 AS(SDLK_LEFTBRACKET, WKC_L_BRACKET),
00277 AS(SDLK_BACKSLASH, WKC_BACKSLASH),
00278 AS(SDLK_RIGHTBRACKET, WKC_R_BRACKET),
00279
00280 AS(SDLK_QUOTE, WKC_SINGLEQUOTE),
00281 AS(SDLK_COMMA, WKC_COMMA),
00282 AS(SDLK_MINUS, WKC_MINUS),
00283 AS(SDLK_PERIOD, WKC_PERIOD)
00284 };
00285
00286 static uint32 ConvertSdlKeyIntoMy(SDL_keysym *sym)
00287 {
00288 const VkMapping *map;
00289 uint key = 0;
00290
00291 for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
00292 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
00293 key = sym->sym - map->vk_from + map->map_to;
00294 break;
00295 }
00296 }
00297
00298
00299 #if defined(WIN32) || defined(__OS2__)
00300 if (sym->scancode == 41) key = WKC_BACKQUOTE;
00301 #elif defined(__APPLE__)
00302 if (sym->scancode == 10) key = WKC_BACKQUOTE;
00303 #elif defined(__MORPHOS__)
00304 if (sym->scancode == 0) key = WKC_BACKQUOTE;
00305 #elif defined(__BEOS__)
00306 if (sym->scancode == 17) key = WKC_BACKQUOTE;
00307 #elif defined(__SVR4) && defined(__sun)
00308 if (sym->scancode == 60) key = WKC_BACKQUOTE;
00309 if (sym->scancode == 49) key = WKC_BACKSPACE;
00310 #elif defined(__sgi__)
00311 if (sym->scancode == 22) key = WKC_BACKQUOTE;
00312 #else
00313 if (sym->scancode == 49) key = WKC_BACKQUOTE;
00314 #endif
00315
00316
00317 if (sym->mod & KMOD_META) key |= WKC_META;
00318 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
00319 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
00320 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
00321
00322 return (key << 16) + sym->unicode;
00323 }
00324
00325 static int PollEvent()
00326 {
00327 SDL_Event ev;
00328
00329 if (!SDL_CALL SDL_PollEvent(&ev)) return -2;
00330
00331 switch (ev.type) {
00332 case SDL_MOUSEMOTION:
00333 if (_cursor.fix_at) {
00334 int dx = ev.motion.x - _cursor.pos.x;
00335 int dy = ev.motion.y - _cursor.pos.y;
00336 if (dx != 0 || dy != 0) {
00337 _cursor.delta.x = dx;
00338 _cursor.delta.y = dy;
00339 SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
00340 }
00341 } else {
00342 _cursor.delta.x = ev.motion.x - _cursor.pos.x;
00343 _cursor.delta.y = ev.motion.y - _cursor.pos.y;
00344 _cursor.pos.x = ev.motion.x;
00345 _cursor.pos.y = ev.motion.y;
00346 _cursor.dirty = true;
00347 }
00348 HandleMouseEvents();
00349 break;
00350
00351 case SDL_MOUSEBUTTONDOWN:
00352 if (_rightclick_emulate && SDL_CALL SDL_GetModState() & KMOD_CTRL) {
00353 ev.button.button = SDL_BUTTON_RIGHT;
00354 }
00355
00356 switch (ev.button.button) {
00357 case SDL_BUTTON_LEFT:
00358 _left_button_down = true;
00359 break;
00360
00361 case SDL_BUTTON_RIGHT:
00362 _right_button_down = true;
00363 _right_button_clicked = true;
00364 break;
00365
00366 case SDL_BUTTON_WHEELUP: _cursor.wheel--; break;
00367 case SDL_BUTTON_WHEELDOWN: _cursor.wheel++; break;
00368
00369 default: break;
00370 }
00371 HandleMouseEvents();
00372 break;
00373
00374 case SDL_MOUSEBUTTONUP:
00375 if (_rightclick_emulate) {
00376 _right_button_down = false;
00377 _left_button_down = false;
00378 _left_button_clicked = false;
00379 } else if (ev.button.button == SDL_BUTTON_LEFT) {
00380 _left_button_down = false;
00381 _left_button_clicked = false;
00382 } else if (ev.button.button == SDL_BUTTON_RIGHT) {
00383 _right_button_down = false;
00384 }
00385 HandleMouseEvents();
00386 break;
00387
00388 case SDL_ACTIVEEVENT:
00389 if (!(ev.active.state & SDL_APPMOUSEFOCUS)) break;
00390
00391 if (ev.active.gain) {
00392 _cursor.in_window = true;
00393 } else {
00394 UndrawMouseCursor();
00395 _cursor.in_window = false;
00396 }
00397 break;
00398
00399 case SDL_QUIT:
00400 HandleExitGameRequest();
00401 break;
00402
00403 case SDL_KEYDOWN:
00404 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_META)) &&
00405 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
00406 ToggleFullScreen(!_fullscreen);
00407 } else {
00408 HandleKeypress(ConvertSdlKeyIntoMy(&ev.key.keysym));
00409 }
00410 break;
00411
00412 case SDL_VIDEORESIZE: {
00413 int w = max(ev.resize.w, 64);
00414 int h = max(ev.resize.h, 64);
00415 ChangeResInGame(w, h);
00416 break;
00417 }
00418 }
00419 return -1;
00420 }
00421
00422 const char *VideoDriver_SDL::Start(const char * const *parm)
00423 {
00424 char buf[30];
00425
00426 const char *s = SdlOpen(SDL_INIT_VIDEO);
00427 if (s != NULL) return s;
00428
00429 GetVideoModes();
00430 if (!CreateMainSurface(_cur_resolution.width, _cur_resolution.height)) {
00431 return SDL_CALL SDL_GetError();
00432 }
00433
00434 SDL_CALL SDL_VideoDriverName(buf, 30);
00435 DEBUG(driver, 1, "SDL: using driver '%s'", buf);
00436
00437 MarkWholeScreenDirty();
00438
00439 SDL_CALL SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
00440 SDL_CALL SDL_EnableUNICODE(1);
00441 return NULL;
00442 }
00443
00444 void VideoDriver_SDL::Stop()
00445 {
00446 SdlClose(SDL_INIT_VIDEO);
00447 }
00448
00449 void VideoDriver_SDL::MainLoop()
00450 {
00451 uint32 cur_ticks = SDL_CALL SDL_GetTicks();
00452 uint32 last_cur_ticks = cur_ticks;
00453 uint32 next_tick = cur_ticks + 30;
00454 uint32 pal_tick = 0;
00455 uint32 mod;
00456 int numkeys;
00457 Uint8 *keys;
00458
00459 for (;;) {
00460 uint32 prev_cur_ticks = cur_ticks;
00461 InteractiveRandom();
00462
00463 while (PollEvent() == -1) {}
00464 if (_exit_game) return;
00465
00466 mod = SDL_CALL SDL_GetModState();
00467 keys = SDL_CALL SDL_GetKeyState(&numkeys);
00468 #if defined(_DEBUG)
00469 if (_shift_pressed)
00470 #else
00471
00472
00473 if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
00474 #endif
00475 {
00476 if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
00477 } else if (_fast_forward & 2) {
00478 _fast_forward = 0;
00479 }
00480
00481 cur_ticks = SDL_CALL SDL_GetTicks();
00482 if (cur_ticks >= next_tick || (_fast_forward && !_pause_game) || cur_ticks < prev_cur_ticks) {
00483 _realtime_tick += cur_ticks - last_cur_ticks;
00484 last_cur_ticks = cur_ticks;
00485 next_tick = cur_ticks + 30;
00486
00487 bool old_ctrl_pressed = _ctrl_pressed;
00488
00489 _ctrl_pressed = !!(mod & KMOD_CTRL);
00490 _shift_pressed = !!(mod & KMOD_SHIFT);
00491
00492
00493 _dirkeys =
00494 (keys[SDLK_LEFT] ? 1 : 0) |
00495 (keys[SDLK_UP] ? 2 : 0) |
00496 (keys[SDLK_RIGHT] ? 4 : 0) |
00497 (keys[SDLK_DOWN] ? 8 : 0);
00498
00499 if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
00500
00501 GameLoop();
00502
00503 _screen.dst_ptr = _sdl_screen->pixels;
00504 UpdateWindows();
00505 if (++pal_tick > 4) {
00506 CheckPaletteAnim();
00507 pal_tick = 1;
00508 }
00509 DrawSurfaceToScreen();
00510 } else {
00511 SDL_CALL SDL_Delay(1);
00512 _screen.dst_ptr = _sdl_screen->pixels;
00513 NetworkDrawChatMessage();
00514 DrawMouseCursor();
00515 DrawSurfaceToScreen();
00516 }
00517 }
00518 }
00519
00520 bool VideoDriver_SDL::ChangeResolution(int w, int h)
00521 {
00522 return CreateMainSurface(w, h);
00523 }
00524
00525 bool VideoDriver_SDL::ToggleFullscreen(bool fullscreen)
00526 {
00527 _fullscreen = fullscreen;
00528 GetVideoModes();
00529 if (_num_resolutions == 0 || !this->ChangeResolution(_cur_resolution.width, _cur_resolution.height)) {
00530
00531 _fullscreen ^= true;
00532 return false;
00533 }
00534 return true;
00535 }
00536
00537 #endif