aboutsummaryrefslogtreecommitdiff
path: root/backends/sdl/sdl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backends/sdl/sdl.cpp')
-rw-r--r--backends/sdl/sdl.cpp586
1 files changed, 279 insertions, 307 deletions
diff --git a/backends/sdl/sdl.cpp b/backends/sdl/sdl.cpp
index 1afba41d50..2560388ed3 100644
--- a/backends/sdl/sdl.cpp
+++ b/backends/sdl/sdl.cpp
@@ -20,373 +20,345 @@
*
*/
-#include "sdl-common.h"
+#include "backends/sdl/sdl-common.h"
+#include "common/config-manager.h"
#include "common/util.h"
-class OSystem_SDL : public OSystem_SDL_Common {
-public:
- OSystem_SDL();
-
- // Update the dirty areas of the screen
- void internUpdateScreen();
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
-protected:
- SDL_Surface *_hwscreen; // hardware screen
+#include "scummvm.xpm"
- virtual void load_gfx_mode();
- virtual void unload_gfx_mode();
- virtual bool save_screenshot(const char *filename);
- virtual void hotswap_gfx_mode();
-
- virtual void setFullscreenMode(bool enable);
-};
-OSystem_SDL_Common *OSystem_SDL_Common::create_intern() {
+OSystem *OSystem_SDL_create() {
return new OSystem_SDL();
}
-OSystem_SDL::OSystem_SDL()
- : _hwscreen(0)
-{
-}
+void OSystem_SDL::init_intern() {
-void OSystem_SDL::load_gfx_mode() {
- _forceFull = true;
- _mode_flags |= DF_UPDATE_EXPAND_1_PIXEL;
+ int joystick_num = ConfMan.getInt("joystick_num");
+ uint32 sdlFlags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
- _tmpscreen = NULL;
- _tmpScreenWidth = (_screenWidth + 3);
-
- switch(_mode) {
- case GFX_NORMAL:
- _scaleFactor = 1;
- _scaler_proc = Normal1x;
- break;
- case GFX_DOUBLESIZE:
- _scaleFactor = 2;
- _scaler_proc = Normal2x;
- break;
- case GFX_TRIPLESIZE:
- _scaleFactor = 3;
- _scaler_proc = Normal3x;
- break;
+ if (joystick_num > -1)
+ sdlFlags |= SDL_INIT_JOYSTICK;
- case GFX_2XSAI:
- _scaleFactor = 2;
- _scaler_proc = _2xSaI;
- break;
- case GFX_SUPER2XSAI:
- _scaleFactor = 2;
- _scaler_proc = Super2xSaI;
- break;
- case GFX_SUPEREAGLE:
- _scaleFactor = 2;
- _scaler_proc = SuperEagle;
- break;
- case GFX_ADVMAME2X:
- _scaleFactor = 2;
- _scaler_proc = AdvMame2x;
- break;
- case GFX_ADVMAME3X:
- _scaleFactor = 3;
- _scaler_proc = AdvMame3x;
- break;
- case GFX_HQ2X:
- _scaleFactor = 2;
- _scaler_proc = HQ2x;
- break;
- case GFX_HQ3X:
- _scaleFactor = 3;
- _scaler_proc = HQ3x;
- break;
- case GFX_TV2X:
- _scaleFactor = 2;
- _scaler_proc = TV2x;
- break;
- case GFX_DOTMATRIX:
- _scaleFactor = 2;
- _scaler_proc = DotMatrix;
- break;
-
- default:
- error("unknown gfx mode %d", _mode);
+ if (SDL_Init(sdlFlags) == -1) {
+ error("Could not initialize SDL: %s", SDL_GetError());
}
- //
- // Create the surface that contains the 8 bit game data
- //
- _screen = SDL_CreateRGBSurface(SDL_SWSURFACE, _screenWidth, _screenHeight, 8, 0, 0, 0, 0);
- if (_screen == NULL)
- error("_screen failed");
-
- //
- // Create the surface that contains the scaled graphics in 16 bit mode
- //
-
- _hwscreen = SDL_SetVideoMode(_screenWidth * _scaleFactor, (_adjustAspectRatio ? 240 : _screenHeight) * _scaleFactor, 16,
- _full_screen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
- );
- if (_hwscreen == NULL) {
- // DON'T use error(), as this tries to bring up the debug
- // console, which WON'T WORK now that _hwscreen is hosed.
-
- // FIXME: We should be able to continue the game without
- // shutting down or bringing up the debug console, but at
- // this point we've already screwed up all our member vars.
- // We need to find a way to call SDL_SetVideoMode *before*
- // that happens and revert to all the old settings if we
- // can't pull off the switch to the new settings.
- //
- // Fingolfin says: the "easy" way to do that is not to modify
- // the member vars before we are sure everything is fine. Think
- // of "transactions, commit, rollback" style... we use local vars
- // in place of the member vars, do everything etc. etc.. In case
- // of a failure, rollback is trivial. Only if everything worked fine
- // do we "commit" the changed values to the member vars.
- warning("SDL_SetVideoMode says we can't switch to that mode");
- quit();
- }
+ _graphicsMutex = createMutex();
- //
- // Create the surface used for the graphics in 16 bit before scaling, and also the overlay
- //
-
- // Distinguish 555 and 565 mode
- if (_hwscreen->format->Rmask == 0x7C00)
- InitScalers(555);
- else
- InitScalers(565);
+ SDL_ShowCursor(SDL_DISABLE);
- // Need some extra bytes around when using 2xSaI
- uint16 *tmp_screen = (uint16 *)calloc(_tmpScreenWidth * (_screenHeight + 3), sizeof(uint16));
- _tmpscreen = SDL_CreateRGBSurfaceFrom(tmp_screen,
- _tmpScreenWidth, _screenHeight + 3, 16, _tmpScreenWidth * 2,
- _hwscreen->format->Rmask,
- _hwscreen->format->Gmask,
- _hwscreen->format->Bmask,
- _hwscreen->format->Amask);
-
- if (_tmpscreen == NULL)
- error("_tmpscreen failed");
-
- // keyboard cursor control, some other better place for it?
- km.x_max = _screenWidth * _scaleFactor - 1;
- km.y_max = (_adjustAspectRatio ? 240 : _screenHeight) * _scaleFactor - 1;
- km.delay_time = 25;
- km.last_time = 0;
-}
+ // Enable unicode support if possible
+ SDL_EnableUNICODE(1);
-void OSystem_SDL::unload_gfx_mode() {
- if (_screen) {
- SDL_FreeSurface(_screen);
- _screen = NULL;
- }
+ cksum_valid = false;
+ _mode = GFX_DOUBLESIZE;
+ _full_screen = ConfMan.getBool("fullscreen");
+ _adjustAspectRatio = ConfMan.getBool("aspect_ratio");
+ _mode_flags = 0;
- if (_hwscreen) {
- SDL_FreeSurface(_hwscreen);
- _hwscreen = NULL;
- }
- if (_tmpscreen) {
- free(_tmpscreen->pixels);
- SDL_FreeSurface(_tmpscreen);
- _tmpscreen = NULL;
+#ifndef MACOSX // Don't set icon on OS X, as we use a nicer external icon there
+ // Setup the icon
+ setup_icon();
+#endif
+
+ // enable joystick
+ if (joystick_num > -1 && SDL_NumJoysticks() > 0) {
+ printf("Using joystick: %s\n", SDL_JoystickName(0));
+ init_joystick(joystick_num);
}
}
-void OSystem_SDL::hotswap_gfx_mode() {
- if (!_screen)
- return;
+OSystem_SDL::OSystem_SDL()
+ : _hwscreen(0), _screen(0), _screenWidth(0), _screenHeight(0),
+ _tmpscreen(0), _tmpScreenWidth(0), _overlayVisible(false),
+ _cdrom(0), _scaler_proc(0), _modeChanged(false), _dirty_checksums(0),
+ _mouseVisible(false), _mouseDrawn(false), _mouseData(0),
+ _mouseHotspotX(0), _mouseHotspotY(0),
+ _currentShakePos(0), _newShakePos(0),
+ _paletteDirtyStart(0), _paletteDirtyEnd(0),
+ _graphicsMutex(0) {
+
+ // allocate palette storage
+ _currentPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256);
+
+ // allocate the dirty rect storage
+ _mouseBackup = (byte *)malloc(MAX_MOUSE_W * MAX_MOUSE_H * MAX_SCALING * 2);
+
+ // reset mouse state
+ memset(&km, 0, sizeof(km));
+
+ init_intern();
+}
- // Keep around the old _screen & _tmpscreen so we can restore the screen data
- // after the mode switch.
- SDL_Surface *old_screen = _screen;
- SDL_Surface *old_tmpscreen = _tmpscreen;
+OSystem_SDL::~OSystem_SDL() {
+// unload_gfx_mode();
- // Release the HW screen surface
- SDL_FreeSurface(_hwscreen);
+ if (_dirty_checksums)
+ free(_dirty_checksums);
+ free(_currentPalette);
+ free(_mouseBackup);
+ deleteMutex(_graphicsMutex);
- // Setup the new GFX mode
- load_gfx_mode();
+ SDL_ShowCursor(SDL_ENABLE);
+ SDL_Quit();
+}
- // reset palette
- SDL_SetColors(_screen, _currentPalette, 0, 256);
+uint32 OSystem_SDL::get_msecs() {
+ return SDL_GetTicks();
+}
- // Restore old screen content
- SDL_BlitSurface(old_screen, NULL, _screen, NULL);
- SDL_BlitSurface(old_tmpscreen, NULL, _tmpscreen, NULL);
-
- // Free the old surfaces
- SDL_FreeSurface(old_screen);
- free(old_tmpscreen->pixels);
- SDL_FreeSurface(old_tmpscreen);
+void OSystem_SDL::delay_msecs(uint msecs) {
+ SDL_Delay(msecs);
+}
- // Blit everything to the screen
- internUpdateScreen();
-
- // Make sure that an EVENT_SCREEN_CHANGED gets sent later
- _modeChanged = true;
+void OSystem_SDL::set_timer(TimerProc callback, int timer) {
+ SDL_SetTimer(timer, (SDL_TimerCallback) callback);
}
-void OSystem_SDL::internUpdateScreen() {
- assert(_hwscreen != NULL);
+void OSystem_SDL::setWindowCaption(const char *caption) {
+ SDL_WM_SetCaption(caption, caption);
+}
- // If the shake position changed, fill the dirty area with blackness
- if (_currentShakePos != _newShakePos) {
- SDL_Rect blackrect = {0, 0, _screenWidth * _scaleFactor, _newShakePos * _scaleFactor};
+bool OSystem_SDL::hasFeature(Feature f) {
+ return
+ (f == kFeatureFullscreenMode) ||
+ (f == kFeatureAspectRatioCorrection) ||
+ (f == kFeatureAutoComputeDirtyRects);
+}
- if (_adjustAspectRatio)
- blackrect.h = real2Aspect(blackrect.h - 1) + 1;
+void OSystem_SDL::setFeatureState(Feature f, bool enable) {
+ Common::StackLock lock(_graphicsMutex, this);
- SDL_FillRect(_hwscreen, &blackrect, 0);
+ switch (f) {
+ case kFeatureFullscreenMode:
+ setFullscreenMode(enable);
+ break;
+ case kFeatureAspectRatioCorrection:
+ if (_screenHeight == 200 && _adjustAspectRatio != enable) {
+ //assert(_hwscreen != 0);
+ _adjustAspectRatio ^= true;
+ hotswap_gfx_mode();
+ }
+ break;
+ case kFeatureAutoComputeDirtyRects:
+ if (enable)
+ _mode_flags |= DF_WANT_RECT_OPTIM;
+ else
+ _mode_flags &= ~DF_WANT_RECT_OPTIM;
+ break;
+ default:
+ break;
+ }
+}
- _currentShakePos = _newShakePos;
+bool OSystem_SDL::getFeatureState(Feature f) {
+ switch (f) {
+ case kFeatureFullscreenMode:
+ return _full_screen;
+ case kFeatureAspectRatioCorrection:
+ return _adjustAspectRatio;
+ case kFeatureAutoComputeDirtyRects:
+ return _mode_flags & DF_WANT_RECT_OPTIM;
+ default:
+ return false;
+ }
+}
- _forceFull = true;
+void OSystem_SDL::quit() {
+ if(_cdrom) {
+ SDL_CDStop(_cdrom);
+ SDL_CDClose(_cdrom);
}
+ unload_gfx_mode();
- // Make sure the mouse is drawn, if it should be drawn.
- draw_mouse();
-
- // Check whether the palette was changed in the meantime and update the
- // screen surface accordingly.
- if (_paletteDirtyEnd != 0) {
- SDL_SetColors(_screen, _currentPalette + _paletteDirtyStart,
- _paletteDirtyStart,
- _paletteDirtyEnd - _paletteDirtyStart);
-
- _paletteDirtyEnd = 0;
+ SDL_ShowCursor(SDL_ENABLE);
+ SDL_Quit();
- _forceFull = true;
- }
+ exit(0);
+}
- // Force a full redraw if requested
- if (_forceFull) {
- _num_dirty_rects = 1;
+void OSystem_SDL::setup_icon() {
+ int w, h, ncols, nbytes, i;
+ unsigned int rgba[256], icon[32 * 32];
+ unsigned char mask[32][4];
- _dirty_rect_list[0].x = 0;
- _dirty_rect_list[0].y = 0;
- _dirty_rect_list[0].w = _screenWidth;
- _dirty_rect_list[0].h = _screenHeight;
+ sscanf(scummvm_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes);
+ if ((w != 32) || (h != 32) || (ncols > 255) || (nbytes > 1)) {
+ warning("Could not load the icon (%d %d %d %d)", w, h, ncols, nbytes);
+ return;
}
-
- // Only draw anything if necessary
- if (_num_dirty_rects > 0) {
-
- SDL_Rect *r;
- SDL_Rect dst;
- uint32 srcPitch, dstPitch;
- SDL_Rect *last_rect = _dirty_rect_list + _num_dirty_rects;
-
- if (_scaler_proc == Normal1x && !_adjustAspectRatio) {
- SDL_Surface *target = _overlayVisible ? _tmpscreen : _screen;
- for (r = _dirty_rect_list; r != last_rect; ++r) {
- dst = *r;
-
- if (_overlayVisible) {
- // FIXME: I don't understand why this is necessary...
- dst.x--;
- dst.y--;
- }
- dst.y += _currentShakePos;
- if (SDL_BlitSurface(target, r, _hwscreen, &dst) != 0)
- error("SDL_BlitSurface failed: %s", SDL_GetError());
- }
+ for (i = 0; i < ncols; i++) {
+ unsigned char code;
+ char color[32];
+ unsigned int col;
+ sscanf(scummvm_icon[1 + i], "%c c %s", &code, color);
+ if (!strcmp(color, "None"))
+ col = 0x00000000;
+ else if (!strcmp(color, "black"))
+ col = 0xFF000000;
+ else if (color[0] == '#') {
+ sscanf(color + 1, "%06x", &col);
+ col |= 0xFF000000;
} else {
- if (!_overlayVisible) {
- for (r = _dirty_rect_list; r != last_rect; ++r) {
- dst = *r;
- dst.x++; // Shift rect by one since 2xSai needs to acces the data around
- dst.y++; // any pixel to scale it, and we want to avoid mem access crashes.
- if (SDL_BlitSurface(_screen, r, _tmpscreen, &dst) != 0)
- error("SDL_BlitSurface failed: %s", SDL_GetError());
- }
+ warning("Could not load the icon (%d %s - %s) ", code, color, scummvm_icon[1 + i]);
+ return;
+ }
+
+ rgba[code] = col;
+ }
+ memset(mask, 0, sizeof(mask));
+ for (h = 0; h < 32; h++) {
+ const char *line = scummvm_icon[1 + ncols + h];
+ for (w = 0; w < 32; w++) {
+ icon[w + 32 * h] = rgba[(int)line[w]];
+ if (rgba[(int)line[w]] & 0xFF000000) {
+ mask[h][w >> 3] |= 1 << (7 - (w & 0x07));
}
+ }
+ }
- SDL_LockSurface(_tmpscreen);
- SDL_LockSurface(_hwscreen);
+ SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, 32, 32, 32, 32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
+ SDL_WM_SetIcon(sdl_surf, (unsigned char *) mask);
+ SDL_FreeSurface(sdl_surf);
+}
- srcPitch = _tmpscreen->pitch;
- dstPitch = _hwscreen->pitch;
+OSystem::MutexRef OSystem_SDL::createMutex(void) {
+ return (MutexRef) SDL_CreateMutex();
+}
- for (r = _dirty_rect_list; r != last_rect; ++r) {
- register int dst_y = r->y + _currentShakePos;
- register int dst_h = 0;
- register int orig_dst_y = 0;
+void OSystem_SDL::lockMutex(MutexRef mutex) {
+ SDL_mutexP((SDL_mutex *) mutex);
+}
- if (dst_y < _screenHeight) {
- dst_h = r->h;
- if (dst_h > _screenHeight - dst_y)
- dst_h = _screenHeight - dst_y;
+void OSystem_SDL::unlockMutex(MutexRef mutex) {
+ SDL_mutexV((SDL_mutex *) mutex);
+}
- dst_y *= _scaleFactor;
+void OSystem_SDL::deleteMutex(MutexRef mutex) {
+ SDL_DestroyMutex((SDL_mutex *) mutex);
+}
- if (_adjustAspectRatio) {
- orig_dst_y = dst_y;
- dst_y = real2Aspect(dst_y);
- }
+#pragma mark -
+#pragma mark --- Audio ---
+#pragma mark -
- _scaler_proc((byte *)_tmpscreen->pixels + (r->x * 2 + 2) + (r->y + 1) * srcPitch, srcPitch,
- (byte *)_hwscreen->pixels + r->x * 2 * _scaleFactor + dst_y * dstPitch, dstPitch, r->w, dst_h);
- }
+bool OSystem_SDL::setSoundCallback(SoundProc proc, void *param) {
+ SDL_AudioSpec desired;
- r->x *= _scaleFactor;
- r->y = dst_y;
- r->w *= _scaleFactor;
- r->h = dst_h * _scaleFactor;
+ memset(&desired, 0, sizeof(desired));
- if (_adjustAspectRatio && orig_dst_y / _scaleFactor < _screenHeight)
- r->h = stretch200To240((uint8 *) _hwscreen->pixels, dstPitch, r->w, r->h, r->x, r->y, orig_dst_y);
- }
- SDL_UnlockSurface(_tmpscreen);
- SDL_UnlockSurface(_hwscreen);
- }
+ desired.freq = SAMPLES_PER_SEC;
+ desired.format = AUDIO_S16SYS;
+ desired.channels = 2;
+ desired.samples = 2048;
+ desired.callback = proc;
+ desired.userdata = param;
+ if (SDL_OpenAudio(&desired, NULL) != 0) {
+ return false;
+ }
+ SDL_PauseAudio(0);
+ return true;
+}
- // Readjust the dirty rect list in case we are doing a full update.
- // This is necessary if shaking is active.
- if (_forceFull) {
- _dirty_rect_list[0].y = 0;
- _dirty_rect_list[0].h = (_adjustAspectRatio ? 240 : _screenHeight) * _scaleFactor;
- }
+void OSystem_SDL::clearSoundCallback() {
+ SDL_CloseAudio();
+}
+
+int OSystem_SDL::getOutputSampleRate() const {
+ return SAMPLES_PER_SEC;
+}
- // Finally, blit all our changes to the screen
- SDL_UpdateRects(_hwscreen, _num_dirty_rects, _dirty_rect_list);
+#pragma mark -
+#pragma mark --- CD Audio ---
+#pragma mark -
+
+bool OSystem_SDL::openCD(int drive) {
+ if (SDL_InitSubSystem(SDL_INIT_CDROM) == -1)
+ _cdrom = NULL;
+ else {
+ _cdrom = SDL_CDOpen(drive);
+ // Did it open? Check if _cdrom is NULL
+ if (!_cdrom) {
+ warning("Couldn't open drive: %s", SDL_GetError());
+ } else {
+ cd_num_loops = 0;
+ cd_stop_time = 0;
+ cd_end_time = 0;
+ }
}
+
+ return (_cdrom != NULL);
+}
- _num_dirty_rects = 0;
- _forceFull = false;
+void OSystem_SDL::stop_cdrom() { /* Stop CD Audio in 1/10th of a second */
+ cd_stop_time = SDL_GetTicks() + 100;
+ cd_num_loops = 0;
}
-bool OSystem_SDL::save_screenshot(const char *filename) {
- assert(_hwscreen != NULL);
+void OSystem_SDL::play_cdrom(int track, int num_loops, int start_frame, int duration) {
+ if (!num_loops && !start_frame)
+ return;
- Common::StackLock lock(_graphicsMutex, this); // Lock the mutex until this function ends
- SDL_SaveBMP(_hwscreen, filename);
- return true;
+ if (!_cdrom)
+ return;
+
+ if (duration > 0)
+ duration += 5;
+
+ cd_track = track;
+ cd_num_loops = num_loops;
+ cd_start_frame = start_frame;
+
+ SDL_CDStatus(_cdrom);
+ if (start_frame == 0 && duration == 0)
+ SDL_CDPlayTracks(_cdrom, track, 0, 1, 0);
+ else
+ SDL_CDPlayTracks(_cdrom, track, start_frame, 0, duration);
+ cd_duration = duration;
+ cd_stop_time = 0;
+ cd_end_time = SDL_GetTicks() + _cdrom->track[track].length * 1000 / CD_FPS;
}
-void OSystem_SDL::setFullscreenMode(bool enable) {
- if (_full_screen != enable) {
- assert(_hwscreen != 0);
- _full_screen ^= true;
+bool OSystem_SDL::poll_cdrom() {
+ if (!_cdrom)
+ return false;
- if (_mouseDrawn)
- undraw_mouse();
-
-#if defined(MACOSX) && !SDL_VERSION_ATLEAST(1, 2, 6)
- // On OS X, SDL_WM_ToggleFullScreen is currently not implemented. Worse,
- // before SDL 1.2.6 it always returned -1 (which would indicate a
- // successful switch). So we simply don't call it at all and use
- // hotswap_gfx_mode() directly to switch to fullscreen mode.
- hotswap_gfx_mode();
-#else
- if (!SDL_WM_ToggleFullScreen(_hwscreen)) {
- // if ToggleFullScreen fails, achieve the same effect with hotswap gfx mode
- hotswap_gfx_mode();
- } else {
- // Make sure that an EVENT_SCREEN_CHANGED gets sent later
- _modeChanged = true;
- }
-#endif
+ return (cd_num_loops != 0 && (SDL_GetTicks() < cd_end_time || SDL_CDStatus(_cdrom) != CD_STOPPED));
+}
+
+void OSystem_SDL::update_cdrom() {
+ if (!_cdrom)
+ return;
+
+ if (cd_stop_time != 0 && SDL_GetTicks() >= cd_stop_time) {
+ SDL_CDStop(_cdrom);
+ cd_num_loops = 0;
+ cd_stop_time = 0;
+ return;
+ }
+
+ if (cd_num_loops == 0 || SDL_GetTicks() < cd_end_time)
+ return;
+
+ if (cd_num_loops != 1 && SDL_CDStatus(_cdrom) != CD_STOPPED) {
+ // Wait another second for it to be done
+ cd_end_time += 1000;
+ return;
+ }
+
+ if (cd_num_loops > 0)
+ cd_num_loops--;
+
+ if (cd_num_loops != 0) {
+ if (cd_start_frame == 0 && cd_duration == 0)
+ SDL_CDPlayTracks(_cdrom, cd_track, 0, 1, 0);
+ else
+ SDL_CDPlayTracks(_cdrom, cd_track, cd_start_frame, 0, cd_duration);
+ cd_end_time = SDL_GetTicks() + _cdrom->track[cd_track].length * 1000 / CD_FPS;
}
}