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