view dmsimple.c @ 597:8a9361179a1a

More work on the video setup screen.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 12 Apr 2013 07:35:10 +0300
parents 695a623eed63
children 71539fc4ba5a
line wrap: on
line source

#include <SDL.h>
#include "dmengine.h"
#include "dmargs.h"
#include "dmtext.h"
#include "dmimage.h"
#include "setupfont.h"
#include "setupimage.h"


static DMEngineData engine;

static DMOptArg optList[] =
{
    { 0, '?', "help",       "Show this help", OPT_NONE },
    { 1, 'v', "verbose",    "Be more verbose", OPT_NONE },
    { 2, 'f', "fs",         "Fullscreen", OPT_NONE },
};

const int optListN = sizeof(optList) / sizeof(optList[0]);



static void argShowHelp()
{
    dmPrintBanner(stdout, dmProgName, "[options]");
    dmArgsPrintHelp(stdout, optList, optListN);
}


static BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    (void) optArg;

    switch (optN)
    {
        case 0:
            argShowHelp();
            exit(0);
            break;

        case 1:
            dmVerbosity++;
            break;
        
        case 2:
            engine.optVFlags |= SDL_FULLSCREEN;
            break;

        default:
            dmError("Unknown option '%s'.\n", currArg);
            return FALSE;
    }
    
    return TRUE;
}


static void engineAudioCallback(void *userdata, Uint8 *stream, int len)
{
    (void) userdata;

    if (engine.paused)
    {
        memset(stream, 0, len);
    }
    else
#ifdef DM_USE_JSS
    {
        if (engine.dev != NULL)
            jvmRenderAudio(engine.dev, stream, len / jvmGetSampleSize(engine.dev));
    }
#endif
#ifdef DM_USE_TREMOR
    if (engine.audioPos + len >= engine.audioRes->rdataSize)
    {
        engine.exitFlag = TRUE;
    }
    else
    {
        memcpy(stream, engine.audioRes->rdata + engine.audioPos, len);
        engine.audioPos += len;
    }
#endif
}


static int engineShowProgress(int loaded, int total)
{
    int dx = 60,
        dh = 20,
        dw = engine.screen->w - (2 * dx),
        dy = (engine.screen->h - dh) / 2;
    
    if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
        return DMERR_INIT_FAIL;
    
    // Draw the progress bar
    dmClearSurface(engine.screen, dmMapRGBA(engine.screen, 0,0,0,0));
    dmFillRect(engine.screen, dx, dy, dx+dw, dy+dh, dmMapRGB(engine.screen, 255,255,255));
    dmFillRect(engine.screen, dx+1, dy+1, dx+dw-1, dy+dh-1, dmMapRGB(engine.screen, 0,0,0));

    if (total > 0)
    {
        dmFillRect(engine.screen,
            dx+3, dy+3,
            dx + 3 + ((dw - 3) * loaded) / total,
            dy + dh - 3,
            dmMapRGB(engine.screen, 200,200,200));
    }

    // Flip screen
    if (SDL_MUSTLOCK(engine.screen) != 0)
        SDL_UnlockSurface(engine.screen);

    SDL_Flip(engine.screen);
    return DMERR_OK;
}


static int engineLoadResources()
{
    int err, loaded = 0, total = 0;

    // First preload outside of the loop
    err = dmres_preload(engine.resources, TRUE, &loaded, &total);

    while ((err = dmres_preload(engine.resources, FALSE, &loaded, &total)) == DMERR_PROGRESS)
    {
        // Show a nice progress bar while loading
        if (total > 0 && (loaded % 2) == 0)
        {
            if ((err = engineShowProgress(loaded, total)) != DMERR_OK)
                return err;
        }
    }
    
    return err;
}


static BOOL engineGenInitializeVideo(int width, int height, int depth, Uint32 flags)
{
    dmPrint(1, "Initializing SDL video %d x %d x %dbpp, flags=0x%08x\n",
        width, height, depth, flags);

    SDL_FreeSurface(engine.screen);

    engine.screen = SDL_SetVideoMode(width, height, depth, flags);
    if (engine.screen == NULL)
    {
        dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError());
        return FALSE;
    }

    SDL_WM_SetCaption(dmProgDesc, dmProgName);

    return TRUE;
}


static BOOL engineInitializeVideo()
{
    return engineGenInitializeVideo(engine.optVidWidth, engine.optVidHeight,
        engine.optVidDepth, engine.optVFlags);
}


int engineVideoSetup()
{
    DMBitmapFont *menuFont = NULL;
    DMResource *file = NULL;
    SDL_Surface *menuBgImage = NULL;
    int result, menuState = -1, nmenuList, menuChoice = 0;
    DMScaledBlitFunc menuBgBlit;
    char menuStr[128];

    // Compute a list of valid modes


    // Open video temporarily
    if (!engineGenInitializeVideo(DM_VSETUP_WIDTH, DM_VSETUP_HEIGHT, 32, SDL_SWSURFACE | SDL_DOUBLEBUF))
        goto out;

    // Fetch and decompress setup image, try regular resources first
    file = dmf_open(engine.resources, "SetupImage.png");
    if (file == NULL)
        file = dmf_create_memio(NULL, "SetupImage", engineSetupImage, sizeof(engineSetupImage));

    menuBgImage = dmLoadImage(file);
    dmf_close(file);
    if (menuBgImage == NULL)
    {
        dmError("Could not instantiate setup screen image, %d: %s\n",
            result, dmErrorStr(result));
        goto out;
    }
    
    if (menuBgImage->w != DM_VSETUP_WIDTH ||
        menuBgImage->h != DM_VSETUP_HEIGHT)
    {
        dmError("Setup screen background image does not match "
            "required dimensions (%dx%d vs %dx%d)\n",
            menuBgImage->w, menuBgImage->h,
            DM_VSETUP_WIDTH, DM_VSETUP_HEIGHT);
        goto out;
    }


    // Load up the bitmap font
    file = dmf_open(engine.resources, "SetupFont.fnt");
    if (file == NULL)
        file = dmf_create_memio(NULL, "SetupFont", engineSetupFont, sizeof(engineSetupFont));

    result = dmLoadBitmapFont(file, &menuFont);
    dmf_close(file);
    if (result != DMERR_OK)
    {
        dmError("Could not instantiate setup screen font, %d: %s\n",
            result, dmErrorStr(result));
        goto out;
    }
    
    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
    menuBgBlit = dmGetScaledBlitFunc(menuBgImage->format, engine.screen->format, DMD_NONE);
    int menuEntryHeight = menuFont->height + 1,
        menuHeight = DM_VSETUP_MENU_HEIGHT / menuEntryHeight;

    // Enter the main loop of the menu
    menuState = 0;
    while (!menuState)
    {
        while (SDL_PollEvent(&engine.event))
        switch (engine.event.type)
        {
            case SDL_KEYDOWN:
                switch (engine.event.key.keysym.sym)
                {
                    case SDLK_ESCAPE:
                        menuState = -1;
                        break;

                    case SDLK_SPACE:
                    case SDLK_RETURN:
                        menuState = 1;
                        break;

                    case SDLK_UP:
                        if (menuChoice > 0)
                            menuChoice--;
                        break;

                    case SDLK_DOWN:
                        if (menuChoice < nmenuList - 1)
                            menuChoice++;
                        break;

                    default:
                        break;
                }

                break;

            case SDL_QUIT:
                menuState = -1;
                break;
        }

        // Draw frame
        if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
        {
            dmError("Can't lock surface.\n");
            goto out;
        }

        // Render the menu
        dmClearSurface(engine.screen, dmMapRGB(engine.screen, 0,0,0));
        menuBgBlit(menuBgImage, 0, 0, engine.screen->w, engine.screen->h, engine.screen);
        
        // XXX/TODO: Some hardcoded bits here ...
        int entry, yc = 0;
        nmenuList = 5;
        
        dmFillRect(engine.screen,
            DM_VSETUP_MENU_XC,
            DM_VSETUP_MENU_YC + (menuChoice * menuEntryHeight),
            DM_VSETUP_MENU_XC + DM_VSETUP_MENU_WIDTH,
            DM_VSETUP_MENU_YC + (menuChoice * menuEntryHeight) + menuEntryHeight + 2,
            dmMapRGBA(engine.screen, 255, 255, 255, 100));
            
        for (entry = 0; entry < nmenuList && entry < menuHeight; entry++)
        {
            snprintf(menuStr, sizeof(menuStr), "SUPERMEGA %d", menuHeight);
            dmDrawBMTextConst(
                engine.screen, menuFont, DMD_TRANSPARENT,
                DM_VSETUP_MENU_XC + 2,
                DM_VSETUP_MENU_YC + 2 + entry * menuEntryHeight,
                menuStr);
        }
        

        // Flip screen
        if (SDL_MUSTLOCK(engine.screen) != 0)
            SDL_UnlockSurface(engine.screen);

        SDL_Flip(engine.screen);
        SDL_Delay(20);
    }

out:
    return menuState;
}


int engineGetVideoAspect(int width, int height)
{
    if (width > 0 && height > 0)
        return (width * 1000) / height;
    else
        return 1;
}


int main(int argc, char *argv[])
{
    int err;
    BOOL initSDL = FALSE;

    memset(&engine, 0, sizeof(engine));

    // Pre-initialization
    if ((err = demoPreInit(&engine)) != DMERR_OK)
        goto error_exit;

    if (!dmArgsProcess(argc, argv, optList, optListN,
        argHandleOpt, NULL, FALSE))
        return DMERR_INIT_FAIL;

    dmPrint(0,
    "%s\n"
    "%s\n"
    "TNSP simple demoengine initializing.\n",
    dmProgDesc, dmProgAuthor);

    dmPrint(0,
    "Using libSDL, "
#ifdef DM_USE_PACKFS
    "zlib, "
#endif
#ifdef DM_USE_TREMOR
    "Tremor Vorbis codec"
#endif
    " and modified stb_image.\n"
    "See README.txt for more information.\n");

    // Initialize resource subsystem
    dmPrint(1, "Initializing resources subsystem.\n");
    if ((err = dmres_init(&engine.resources, engine.optPackFilename, engine.optDataPath, engine.optResFlags, engineClassifier)) != DMERR_OK)
    {
        dmError("Could not initialize resource manager, #%d: %s.\n", err, dmErrorStr(err));
        goto error_exit;
    }

    // Initialize SDL components
    dmPrint(1, "Initializing libSDL.\n");
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0)
    {
        dmError("Could not initialize SDL: %s\n", SDL_GetError());
        goto error_exit;
    }
    initSDL = TRUE;


    // Present video mode selector
    if (engine.optVidAspect <= 0)
        engine.optVidAspect = engineGetVideoAspect(engine.optVidWidth, engine.optVidHeight);

    if (engine.optVidSetup)
    {
        if ((err = engineVideoSetup()) <= 0)
            goto error_exit;
    }

    // Initialize audio parts
    if (engine.optAfmt.freq == 0 && engine.optAfmt.channels == 0)
    {
        // Defaults, if none seem to be set
        engine.optAfmt.freq     = 44100;
        engine.optAfmt.format   = AUDIO_S16SYS;
        engine.optAfmt.channels = 2;
        engine.optAfmt.samples  = engine.optAfmt.freq / 16;
    }

#ifdef DM_USE_JSS
    jssInit();

    switch (engine.optAfmt.format)
    {
        case AUDIO_S16SYS: engine.jss_format = JSS_AUDIO_S16; break;
        case AUDIO_U16SYS: engine.jss_format = JSS_AUDIO_U16; break;
        case AUDIO_S8:     engine.jss_format = JSS_AUDIO_S8; break;
        case AUDIO_U8:     engine.jss_format = JSS_AUDIO_U8; break;
    }

    dmPrint(1, "Initializing miniJSS mixer with fmt=%d, chn=%d, freq=%d\n",
        engine.jss_format, engine.optAfmt.channels, engine.optAfmt.freq);

    if ((engine.dev = jvmInit(engine.jss_format, engine.optAfmt.channels, engine.optAfmt.freq, JMIX_AUTO)) == NULL)
    {
        dmError("jvmInit() returned NULL, voi perkele.\n");
        goto error_exit;
    }

    if ((engine.plr = jmpInit(engine.dev)) == NULL)
    {
        dmError("jmpInit() returned NULL\n");
        goto error_exit;
    }
#endif

    // Initialize SDL audio
    dmPrint(1, "Trying to init SDL audio with: fmt=%d, chn=%d, freq=%d\n",
        engine.optAfmt.format, engine.optAfmt.channels, engine.optAfmt.freq);

    engine.optAfmt.callback = engineAudioCallback;
    
    if (SDL_OpenAudio(&engine.optAfmt, NULL) < 0)
    {
        dmError("Couldn't open SDL audio: %s\n", SDL_GetError());
        // We'll let this pass, as we want to support no-sound.
        // goto error_exit;
    }

    // Initialize SDL video
    if (engine.demoInitPreVideo != NULL &&
       (err = engine.demoInitPreVideo(&engine)) != DMERR_OK)
    {
        dmError("demoInitPreVideo() failed, #%d: %s\n", err, dmErrorStr(err));
        goto error_exit;
    }

    if (!engineInitializeVideo())
        goto error_exit;

    if (engine.demoInitPostVideo != NULL &&
       (err = engine.demoInitPostVideo(&engine)) != DMERR_OK)
    {
        dmError("demoInitPostVideo() failed, #%d: %s\n", err, dmErrorStr(err));
        goto error_exit;
    }

    // Hide cursor
    SDL_ShowCursor(SDL_DISABLE);

    // Load resources
    dmPrint(1, "Loading resources, please wait...\n");
    if ((err = engineLoadResources()) != DMERR_OK)
    {
        dmError("Error loading resources, #%d: %s.\n",
            err, dmErrorStr(err));
        goto error_exit;
    }

    // Final initializations
    if ((err = engine.demoInit(&engine)) != DMERR_OK)
    {
        dmError("Failure in demoInit(), #%d: %s\n",
            err, dmErrorStr(err));
        goto error_exit;
    }

    // Initialize effects
    if ((err = engineInitializeEffects(&engine)) != DMERR_OK)
    {
        dmError("Effects initialization failed, #%d: %s\n",
            err, dmErrorStr(err));
        goto error_exit;
    }

    // Use a timeline, if set
#ifdef DM_USE_TIMELINE
    if (engine.timeline != NULL)
    {
        if ((err = dmLoadTimeline(engine.timeline, &engine.tl)) != DMERR_OK)
        {
            dmError("Error loading timeline, #%d: %s\n", err,
                dmErrorStr(err));
            goto error_exit;
        }

        if ((err = dmPrepareTimeline(engine.tl, engine.ptl)) != DMERR_OK)
        {
            dmError("Error creating prepared timeline, #%d: %s\n",
                err, dmErrorStr(err));
            goto error_exit;
        }
    }
#endif

    dmPrint(1, "Starting up.\n");

    SDL_LockAudio();
    SDL_PauseAudio(0);
    SDL_UnlockAudio();

    engine.startTime = SDL_GetTicks();

    while (!engine.exitFlag)
    {
        while (SDL_PollEvent(&engine.event))
        switch (engine.event.type)
        {
            case SDL_KEYDOWN:
                switch (engine.event.key.keysym.sym)
                {
                    case SDLK_ESCAPE:
                        engine.exitFlag = TRUE;
                        break;
                    
                    case SDLK_SPACE:
                        engine.pauseFlag = !engine.pauseFlag;
                        break;

                    case SDLK_f:
                        engine.optVFlags ^= SDL_FULLSCREEN;
                        if (!engineInitializeVideo())
                            goto error_exit;
                        break;

                    case SDLK_RETURN:
                        if (engine.event.key.keysym.mod & KMOD_ALT)
                        {
                            engine.optVFlags ^= SDL_FULLSCREEN;
                            if (!engineInitializeVideo())
                                goto error_exit;
                        }
                        break;

                    default:
                        break;
                }

                break;

            case SDL_VIDEOEXPOSE:
                break;

            case SDL_QUIT:
                engine.exitFlag = TRUE;
                break;
        }

        // Draw frame
        engine.frameTime = SDL_GetTicks();
        if (engine.pauseFlag != engine.paused)
        {
            engine.paused = engine.pauseFlag;
            engine.pauseTime = engineGetTick(&engine);
        }
        
        if (engine.paused)
        {
            engine.startTime = engine.frameTime - engine.pauseTime;
        }

        if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0)
        {
            dmError("Can't lock surface.\n");
            goto error_exit;
        }

        // Call main tick
        if (engine.demoRender != NULL)
        {
            if ((err = engine.demoRender(&engine)) != DMERR_OK)
                goto error_exit;
        }
#ifdef DM_USE_TIMELINE
        else
        {
            if ((err = dmExecuteTimeline(engine.ptl, engine.screen, engineGetTick(&engine))) != DMERR_OK)
                goto error_exit;
        }
#endif

        // Flip screen
        if (SDL_MUSTLOCK(engine.screen) != 0)
            SDL_UnlockSurface(engine.screen);

        SDL_Flip(engine.screen);
        SDL_Delay(engine.paused ? 100 : 20);

        engine.frameCount++;
    }

    // Print benchmark results
    engine.endTime = SDL_GetTicks();
    dmPrint(1, "%d frames in %d ms, fps = %1.3f\n",
        engine.frameCount, engine.endTime - engine.startTime,
        (float) (engine.frameCount * 1000.0f) / (float) (engine.endTime - engine.startTime));


error_exit:

    dmPrint(1, "Shutting down.\n");
    SDL_ShowCursor(SDL_ENABLE);

    SDL_LockAudio();
    SDL_PauseAudio(1);
#ifdef DM_USE_JSS
    jmpClose(engine.plr);
    jvmClose(engine.dev);
    jssClose();
#endif
    SDL_UnlockAudio();

#ifdef DM_USE_TIMELINE
    dmFreeTimeline(engine.tl);
    dmFreePreparedTimelineData(engine.ptl);
#endif

    engineShutdownEffects(&engine);
    dmres_close(engine.resources);

    if (engine.demoShutdown != NULL)
        engine.demoShutdown(&engine);

    if (initSDL)
        SDL_Quit();

    if (engine.demoQuit != NULL)
        engine.demoQuit(&engine);
    
    return 0;
}