14 #include "../stdafx.h" 15 #include "../openttd.h" 16 #include "../gfx_func.h" 19 #include "../blitter/factory.hpp" 20 #include "../network/network.h" 21 #include "../thread/thread.h" 22 #include "../progress.h" 23 #include "../core/random_func.hpp" 24 #include "../core/math_func.hpp" 25 #include "../fileio_func.h" 26 #include "../framerate_type.h" 30 #include "../safeguards.h" 34 static SDL_Surface *_sdl_screen;
35 static SDL_Surface *_sdl_realscreen;
36 static bool _all_modes;
48 #define MAX_DIRTY_RECTS 100 49 static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
50 static int _num_dirty_rects;
51 static int _use_hwpalette;
52 static int _requested_hwpalette;
56 if (_num_dirty_rects < MAX_DIRTY_RECTS) {
57 _dirty_rects[_num_dirty_rects].x = left;
58 _dirty_rects[_num_dirty_rects].y = top;
59 _dirty_rects[_num_dirty_rects].w = width;
60 _dirty_rects[_num_dirty_rects].h = height;
65 static void UpdatePalette(
bool init =
false)
69 for (
int i = 0; i != _local_palette.
count_dirty; i++) {
78 if (_sdl_screen != _sdl_realscreen && init) {
102 if (_sdl_screen != _sdl_realscreen && !init) {
113 SDL_CALL SDL_BlitSurface(_sdl_screen, NULL, _sdl_realscreen, NULL);
114 SDL_CALL SDL_UpdateRect(_sdl_realscreen, 0, 0, 0, 0);
118 static void InitPalette()
126 static void CheckPaletteAnim()
150 static void DrawSurfaceToScreen()
154 int n = _num_dirty_rects;
157 _num_dirty_rects = 0;
158 if (n > MAX_DIRTY_RECTS) {
159 if (_sdl_screen != _sdl_realscreen) {
160 SDL_CALL SDL_BlitSurface(_sdl_screen, NULL, _sdl_realscreen, NULL);
162 SDL_CALL SDL_UpdateRect(_sdl_realscreen, 0, 0, 0, 0);
164 if (_sdl_screen != _sdl_realscreen) {
165 for (
int i = 0; i < n; i++) {
166 SDL_CALL SDL_BlitSurface(_sdl_screen, &_dirty_rects[i], _sdl_realscreen, &_dirty_rects[i]);
169 SDL_CALL SDL_UpdateRects(_sdl_realscreen, n, _dirty_rects);
173 static void DrawSurfaceToScreenThread(
void *)
185 DrawSurfaceToScreen();
190 _draw_thread->
Exit();
193 static const Dimension _default_resolutions[] = {
207 static void GetVideoModes()
209 SDL_Rect **modes = SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE | SDL_FULLSCREEN);
210 if (modes == NULL)
usererror(
"sdl: no modes available");
212 _all_modes = (SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE | (_fullscreen ? SDL_FULLSCREEN : 0)) == (
void*)-1);
213 if (modes == (
void*)-1) {
215 for (uint i = 0; i <
lengthof(_default_resolutions); i++) {
216 if (SDL_CALL SDL_VideoModeOK(_default_resolutions[i].width, _default_resolutions[i].height, 8, SDL_FULLSCREEN) != 0) {
224 for (
int i = 0; modes[i]; i++) {
225 uint w = modes[i]->w;
226 uint h = modes[i]->h;
228 for (j = 0; j < n; j++) {
243 static void GetAvailableVideoMode(uint *w, uint *h)
258 if (newdelta < delta) {
271 #define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_CALL SDL_RWFromFile(file, "rb"), 1) 274 bool VideoDriver_SDL::CreateMainSurface(uint w, uint h)
276 SDL_Surface *newscreen, *icon;
281 GetAvailableVideoMode(&w, &h);
283 DEBUG(driver, 1,
"SDL: using mode %ux%ux%d", w, h, bpp);
285 if (bpp == 0)
usererror(
"Can't use a blitter that blits 0 bpp for normal visuals");
287 char icon_path[MAX_PATH];
290 icon = SDL_CALL SDL_LoadBMP(icon_path);
293 uint32 rgbmap = SDL_CALL SDL_MapRGB(icon->format, 255, 0, 255);
295 SDL_CALL SDL_SetColorKey(icon, SDL_SRCCOLORKEY, rgbmap);
296 SDL_CALL SDL_WM_SetIcon(icon, NULL);
297 SDL_CALL SDL_FreeSurface(icon);
301 if (_use_hwpalette == 2) {
323 want_hwpalette = bpp == 8 && _fullscreen && _support8bpp ==
S8BPP_HARDWARE;
326 want_hwpalette = _use_hwpalette;
329 if (want_hwpalette)
DEBUG(driver, 1,
"SDL: requesting hardware palette");
332 if (_sdl_screen != NULL && _sdl_screen != _sdl_realscreen) SDL_CALL SDL_FreeSurface(_sdl_screen);
334 if (_sdl_realscreen != NULL) {
335 if (_requested_hwpalette != want_hwpalette) {
344 DEBUG(driver, 0,
"SDL: Restarting SDL video subsystem, to force hwpalette change");
345 SDL_CALL SDL_QuitSubSystem(SDL_INIT_VIDEO);
346 SDL_CALL SDL_InitSubSystem(SDL_INIT_VIDEO);
355 _requested_hwpalette = want_hwpalette;
358 newscreen = SDL_CALL SDL_SetVideoMode(w, h, bpp, SDL_SWSURFACE | (want_hwpalette ? SDL_HWPALETTE : 0) | (_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE));
359 if (newscreen == NULL) {
360 DEBUG(driver, 0,
"SDL: Couldn't allocate a window to draw on");
363 _sdl_realscreen = newscreen;
365 if (bpp == 8 && (_sdl_realscreen->flags & SDL_HWPALETTE) != SDL_HWPALETTE) {
384 DEBUG(driver, 1,
"SDL: using shadow surface");
385 newscreen = SDL_CALL SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp, 0, 0, 0, 0);
386 if (newscreen == NULL) {
387 DEBUG(driver, 0,
"SDL: Couldn't allocate a shadow surface to draw on");
393 _num_dirty_rects = 0;
395 _screen.width = newscreen->w;
396 _screen.height = newscreen->h;
397 _screen.pitch = newscreen->pitch / (bpp / 8);
398 _screen.dst_ptr = newscreen->pixels;
399 _sdl_screen = newscreen;
404 if (_fullscreen) _cursor.
in_window =
true;
411 seprintf(caption,
lastof(caption),
"OpenTTD %s", _openttd_revision);
412 SDL_CALL SDL_WM_SetCaption(caption, caption);
419 bool VideoDriver_SDL::ClaimMousePointer()
421 SDL_CALL SDL_ShowCursor(0);
426 #if SDL_VERSION_ATLEAST(1, 3, 0) 435 #define AS(x, z) {x, 0, z} 436 #define AM(x, y, z, w) {x, (byte)(y - x), z} 440 AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),
442 AS(SDLK_DOWN, WKC_DOWN),
443 AS(SDLK_LEFT, WKC_LEFT),
444 AS(SDLK_RIGHT, WKC_RIGHT),
446 AS(SDLK_HOME, WKC_HOME),
447 AS(SDLK_END, WKC_END),
449 AS(SDLK_INSERT, WKC_INSERT),
450 AS(SDLK_DELETE, WKC_DELETE),
453 AM(SDLK_a, SDLK_z,
'A',
'Z'),
454 AM(SDLK_0, SDLK_9,
'0',
'9'),
456 AS(SDLK_ESCAPE, WKC_ESC),
457 AS(SDLK_PAUSE, WKC_PAUSE),
458 AS(SDLK_BACKSPACE, WKC_BACKSPACE),
460 AS(SDLK_SPACE, WKC_SPACE),
461 AS(SDLK_RETURN, WKC_RETURN),
462 AS(SDLK_TAB, WKC_TAB),
465 AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
468 AM(SDLK_KP0, SDLK_KP9,
'0',
'9'),
469 AS(SDLK_KP_DIVIDE, WKC_NUM_DIV),
470 AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
471 AS(SDLK_KP_MINUS, WKC_NUM_MINUS),
472 AS(SDLK_KP_PLUS, WKC_NUM_PLUS),
473 AS(SDLK_KP_ENTER, WKC_NUM_ENTER),
474 AS(SDLK_KP_PERIOD, WKC_NUM_DECIMAL),
490 static uint ConvertSdlKeyIntoMy(SDL_keysym *sym,
WChar *character)
495 for (map = _vk_mapping; map !=
endof(_vk_mapping); ++map) {
496 if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
497 key = sym->sym - map->vk_from + map->map_to;
503 #if defined(_WIN32) || defined(__OS2__) 504 if (sym->scancode == 41) key = WKC_BACKQUOTE;
505 #elif defined(__APPLE__) 506 if (sym->scancode == 10) key = WKC_BACKQUOTE;
507 #elif defined(__MORPHOS__) 508 if (sym->scancode == 0) key = WKC_BACKQUOTE;
509 #elif defined(__BEOS__) 510 if (sym->scancode == 17) key = WKC_BACKQUOTE;
511 #elif defined(__SVR4) && defined(__sun) 512 if (sym->scancode == 60) key = WKC_BACKQUOTE;
513 if (sym->scancode == 49) key = WKC_BACKSPACE;
514 #elif defined(__sgi__) 515 if (sym->scancode == 22) key = WKC_BACKQUOTE;
517 if (sym->scancode == 49) key = WKC_BACKQUOTE;
521 if (sym->mod & KMOD_META) key |= WKC_META;
522 if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
523 if (sym->mod & KMOD_CTRL) key |= WKC_CTRL;
524 if (sym->mod & KMOD_ALT) key |= WKC_ALT;
526 *character = sym->unicode;
530 int VideoDriver_SDL::PollEvent()
534 if (!SDL_CALL SDL_PollEvent(&ev))
return -2;
537 case SDL_MOUSEMOTION:
539 SDL_CALL SDL_WarpMouse(_cursor.
pos.x, _cursor.
pos.y);
544 case SDL_MOUSEBUTTONDOWN:
546 ev.button.button = SDL_BUTTON_RIGHT;
549 switch (ev.button.button) {
550 case SDL_BUTTON_LEFT:
554 case SDL_BUTTON_RIGHT:
559 case SDL_BUTTON_WHEELUP: _cursor.
wheel--;
break;
560 case SDL_BUTTON_WHEELDOWN: _cursor.
wheel++;
break;
567 case SDL_MOUSEBUTTONUP:
572 }
else if (ev.button.button == SDL_BUTTON_LEFT) {
575 }
else if (ev.button.button == SDL_BUTTON_RIGHT) {
581 case SDL_ACTIVEEVENT:
582 if (!(ev.active.state & SDL_APPMOUSEFOCUS))
break;
584 if (ev.active.gain) {
593 HandleExitGameRequest();
597 if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_META)) &&
598 (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
599 ToggleFullScreen(!_fullscreen);
602 uint keycode = ConvertSdlKeyIntoMy(&ev.key.keysym, &character);
607 case SDL_VIDEORESIZE: {
608 int w =
max(ev.resize.w, 64);
609 int h =
max(ev.resize.h, 64);
610 CreateMainSurface(w, h);
613 case SDL_VIDEOEXPOSE: {
617 _num_dirty_rects = MAX_DIRTY_RECTS + 1;
629 const char *s =
SdlOpen(SDL_INIT_VIDEO);
630 if (s != NULL)
return s;
634 return SDL_CALL SDL_GetError();
637 SDL_CALL SDL_VideoDriverName(buf,
sizeof buf);
638 DEBUG(driver, 1,
"SDL: using driver '%s'", buf);
648 void VideoDriver_SDL::SetupKeyboard()
650 SDL_CALL SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
651 SDL_CALL SDL_EnableUNICODE(1);
661 uint32 cur_ticks = SDL_CALL SDL_GetTicks();
662 uint32 last_cur_ticks = cur_ticks;
674 if (_draw_mutex == NULL) {
697 uint32 prev_cur_ticks = cur_ticks;
700 while (PollEvent() == -1) {}
701 if (_exit_game)
break;
703 mod = SDL_CALL SDL_GetModState();
704 #if SDL_VERSION_ATLEAST(1, 3, 0) 705 keys = SDL_CALL SDL_GetKeyboardState(&numkeys);
707 keys = SDL_CALL SDL_GetKeyState(&numkeys);
714 #if SDL_VERSION_ATLEAST(1, 3, 0) 715 if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
717 if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
721 if (!
_networking && _game_mode != GM_MENU) _fast_forward |= 2;
722 }
else if (_fast_forward & 2) {
726 cur_ticks = SDL_CALL SDL_GetTicks();
727 if (cur_ticks >= next_tick || (_fast_forward && !
_pause_mode) || cur_ticks < prev_cur_ticks) {
729 last_cur_ticks = cur_ticks;
739 #if SDL_VERSION_ATLEAST(1, 3, 0) 740 (keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
741 (keys[SDL_SCANCODE_UP] ? 2 : 0) |
742 (keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
743 (keys[SDL_SCANCODE_DOWN] ? 8 : 0);
745 (keys[SDLK_LEFT] ? 1 : 0) |
746 (keys[SDLK_UP] ? 2 : 0) |
747 (keys[SDLK_RIGHT] ? 4 : 0) |
748 (keys[SDLK_DOWN] ? 8 : 0);
754 if (_draw_mutex != NULL) _draw_mutex->
EndCritical();
764 if (_draw_mutex != NULL) _draw_mutex->
EndCritical();
778 DrawSurfaceToScreen();
782 if (_draw_mutex != NULL) {
788 _draw_thread->
Join();
801 bool ret = CreateMainSurface(w, h);
802 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
809 _fullscreen = fullscreen;
818 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
824 return CreateMainSurface(_screen.width, _screen.height);
834 if (_draw_mutex != NULL) _draw_mutex->
EndCritical(
true);
const char * GetDriverParam(const char *const *parm, const char *name)
Get a string parameter the list of parameters.
bool _networking
are we in networking mode?
uint32 _realtime_tick
The real time in the game.
Point pos
logical mouse position
Information about the currently used palette.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
bool _right_button_down
Is right mouse button pressed?
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
const char * Start(const char *const *param)
Start this driver.
void MainLoop()
Perform the actual drawing.
bool ChangeResolution(int w, int h)
Change the resolution of the window.
Base of the SDL video driver.
Dimension _cur_resolution
The current resolution.
static volatile bool _draw_continue
Should we keep continue drawing?
int _num_resolutions
The number of resolutions.
#define lastof(x)
Get the last element of an fixed size array.
How all blitters should look like.
Subdirectory for all base data (base sets, intro game)
static T max(const T a, const T b)
Returns the maximum of two values.
virtual void EndCritical(bool allow_recursive=false)=0
End of the critical section.
virtual void PostResize()
Post resize event.
Palette animation should be done by video backend (8bpp only!)
virtual void SendSignal()=0
Send a signal and wake the 'thread' that was waiting for it.
bool _left_button_clicked
Is left mouse button clicked?
bool _ctrl_pressed
Is Ctrl pressed?
const char * SdlOpen(uint32 x)
Open the SDL library.
bool _right_button_clicked
Is right mouse button clicked?
The blitter takes care of the palette animation.
char * FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename)
Find a path to the filename in one of the search directories.
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
bool _left_button_down
Is left mouse button pressed?
bool ToggleFullscreen(bool fullscreen)
Change the full screen setting.
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
int wheel
mouse wheel movement
bool UpdateCursorPosition(int x, int y, bool queued_warp)
Update cursor position on mouse movement.
static const uint MILLISECONDS_PER_TICK
The number of milliseconds per game tick.
static ThreadMutex * New()
Create a new mutex.
void HandleKeypress(uint keycode, WChar key)
Handle keyboard input.
byte _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
static ThreadObject * _draw_thread
Thread used to 'draw' to the screen, i.e.
void HandleMouseEvents()
Handle a mouse event from the video driver.
#define lengthof(x)
Return the length of an fixed size array.
PauseModeByte _pause_mode
The current pause mode.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
int first_dirty
The first dirty element.
Palette _cur_palette
Current palette.
bool _shift_pressed
Is Shift pressed?
#define DEBUG(name, level,...)
Output a line of debugging information.
static ThreadMutex * _draw_mutex
Mutex to keep the access to the shared memory controlled.
Dimension _resolutions[32]
List of resolutions.
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
static bool _draw_threaded
Whether the drawing is/may be done in a separate thread.
void HandleCtrlChanged()
State of CONTROL key has changed.
virtual void Join()=0
Join this thread.
void Stop()
Stop this driver.
Speed of painting drawn video buffer.
void ReleaseBlitterLock()
Release any lock(s) required to be held when changing blitters.
void NetworkDrawChatMessage()
Draw the chat message-box.
static Palette _local_palette
Local copy of the palette for use in the drawing thread.
#define endof(x)
Get the end element of an fixed size array.
bool in_window
mouse inside this window, determines drawing logic
bool AfterBlitterChange()
Callback invoked after the blitter was changed.
virtual void WaitForSignal()=0
Wait for a signal to be send.
void AcquireBlitterLock()
Acquire any lock(s) required to be held when changing blitters.
void SdlClose(uint32 x)
Close the SDL library.
virtual uint8 GetScreenDepth()=0
Get the screen depth this blitter works for.
#define AS(ap_name, size_x, size_y, min_year, max_year, catchment, noise, maint_cost, ttdpatch_type, class_id, name, preview)
AirportSpec definition for airports with at least one depot.
int GetDriverParamInt(const char *const *parm, const char *name, int def)
Get an integer parameter the list of parameters.
bool _rightclick_emulate
Whether right clicking is emulated.
void GameSizeChanged()
Size of the application screen changed.
virtual void BeginCritical(bool allow_recursive=false)=0
Begin the critical section.
void MakeDirty(int left, int top, int width, int height)
Mark a particular area dirty.
virtual bool Exit()=0
Exit this thread.
Factory for the SDL video driver.
int count_dirty
The number of dirty elements.
static T Delta(const T a, const T b)
Returns the (absolute) difference between two (scalar) variables.
uint32 WChar
Type for wide characters, i.e.
A Thread Object which works on all our supported OSes.
static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread=NULL, const char *name=NULL)
Create a thread; proc will be called as first function inside the thread, with optional params...
Dimensions (a width and height) of a rectangle in 2D.
Full 8bpp support by OS and hardware.
static bool HasModalProgress()
Check if we are currently in a modal progress state.
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
void UpdateWindows()
Update the continuously changing contents of the windows, such as the viewports.