code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#include <QtGui>

#ifdef Q_OS_LINUX
#include <QX11Info>
#endif

#ifdef MEEGO_EDITION_HARMATTAN
#include <qmeegographicssystemhelper.h>
#endif

#include "gamewindow.h"
#include "pushaudioout.h"
#include "pullaudioout.h"
#include "trace.h" // For debug macros

using namespace GE;

#ifndef GE_NOMOBILITY
QTM_USE_NAMESPACE
#endif

/*!
  \class GameWindow
  \brief QtWidget with native OpenGL ES 2.0 support. Replaces QGLWidget when
         real OpenGL ES is required instead of QtOpenGL.
*/

/*!
  Constructor.
*/
GameWindow::GameWindow(QWidget *parent)
    : QWidget(parent),
      m_prevTime(0),
      m_currentTime(0),
      m_frameTime(0.0f),
      m_fps(0.0f),
      m_paused(true),
      m_timerId(0),
      m_audioOutput(NULL),
      m_hdConnected(false)
#ifndef GE_NOMOBILITY
      ,m_systemDeviceInfo(NULL)
#endif
#ifdef Q_OS_SYMBIAN
    , m_hdmiOutput(NULL),
      m_volumeKeys(NULL)
#endif // Q_OS_SYMBIAN
{
    DEBUG_INFO(this);

    setAutoFillBackground(false);
    setAttribute(Qt::WA_OpaquePaintEvent);
    setAttribute(Qt::WA_NoSystemBackground);
    setAttribute(Qt::WA_NativeWindow);
    setAttribute(Qt::WA_PaintOnScreen, true);
    setAttribute(Qt::WA_StyledBackground, false);
    setAttribute(Qt::WA_PaintUnclipped);

#ifdef GE_USE_MM_KEYS
    QApplication::setAttribute(Qt::AA_CaptureMultimediaKeys);
#endif // GE_USE_MM_KEYS

#ifdef MEEGO_EDITION_HARMATTAN
    QMeeGoGraphicsSystemHelper::setSwitchPolicy(QMeeGoGraphicsSystemHelper::NoSwitch);
#endif // MEEGO_EDITION_HARMATTAN

#ifdef Q_OS_SYMBIAN
    m_volumeKeys = new VolumeKeys(this, this);
#endif // Q_OS_SYMBIAN

#ifndef GE_NOMOBILITY
    m_systemDeviceInfo = new QSystemDeviceInfo(this);
    connect(m_systemDeviceInfo,
        SIGNAL(currentProfileChanged(QSystemDeviceInfo::Profile)),
        this, SLOT(profileChanged(QSystemDeviceInfo::Profile)));
#endif
}

/*!
  Destructor.
*/
GameWindow::~GameWindow()
{
    DEBUG_POINT;

    destroy();

#ifdef Q_OS_SYMBIAN
    delete m_hdmiOutput;
    m_hdmiOutput = NULL;

    delete m_volumeKeys;
    m_volumeKeys = NULL;
#endif // Q_OS_SYMBIAN

#ifndef GE_NOMOBILITY
    delete m_systemDeviceInfo;
#endif

    if (m_timerId)
        killTimer(m_timerId);

    stopAudio();
}

/*!
  Initializes OpenGL.
*/
void GameWindow::create()
{
    DEBUG_POINT;
    createEGL();

    onCreate();
    onInitEGL();

    m_currentTime = getTickCount();
    m_prevTime = m_currentTime;
    m_fps = 0.0f;
    m_frameTime = 0.0f;

    resume();

    DEBUG_INFO("Finished!");
}

/*!
  Called when the game window is to be destroyed.
*/
void GameWindow::destroy()
{
    DEBUG_POINT;

    onFreeEGL();

    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroySurface(eglDisplay, eglSurface);

#ifdef Q_OS_SYMBIAN
    if (m_hdmiOutput)
        m_hdmiOutput->destroyWindow();
#endif // Q_OS_SYMBIAN

    onDestroy();
}

/*!
  Returns true if the current profile is silent.
*/
bool GameWindow::isProfileSilent() const
{
#ifndef GE_NOMOBILITY
    DEBUG_INFO("Current profile:" << m_systemDeviceInfo->currentProfile());

    return m_systemDeviceInfo->currentProfile() ==
        QSystemDeviceInfo::SilentProfile ? true : false;
#else
    return false;
#endif
}

/*!
  Returns the tick count in milliseconds.
*/
unsigned int GameWindow::getTickCount() const
{
    QTime now = QTime::currentTime();
    return (now.hour() * 3600 + now.minute() * 60 + now.second()) * 1000
            + now.msec();
}

/*!
  Pauses the execution.
*/
void GameWindow::pause()
{
    DEBUG_POINT;

    if (m_paused)
        return;

    m_paused = true;
    killTimer(m_timerId);
    stopAudio();
    m_timerId = 0;
    onPause();
}

/*!
  Resumes from the pause mode.
*/
void GameWindow::resume()
{
    DEBUG_POINT;

    if (!m_paused)
        return;

#ifdef Q_OS_SYMBIAN
    // Wait a little while to be able to see the new profile correctly.
    User::After( 500000 );
#endif // Q_OS_SYMBIAN

    m_paused = false;
    startAudio();
    onResume();
    m_timerId = startTimer(0);
}

/*!
  Starts the audio if not started.
*/
void GameWindow::startAudio()
{
    if (m_audioOutput || isProfileSilent())
        return;

    DEBUG_INFO("Starting audio..");
#ifdef GE_PULLMODEAUDIO
    m_audioOutput = new GE::PullAudioOut(&m_audioMixer, this);
#else
    m_audioOutput = new GE::PushAudioOut(&m_audioMixer, this);
#endif
}

/*!
  Stops the audio.
*/
void GameWindow::stopAudio()
{
    if (!m_audioOutput)
        return;

    delete m_audioOutput;
    m_audioOutput = NULL;
}

/*!
  Return true if the audio is enabled.
*/
bool GameWindow::audioEnabled() const
{
    return m_audioOutput ? true : false;
}

/*!
  Returns current audio mixer.
*/
AudioMixer &GameWindow::getMixer()
{
    return m_audioMixer;
}

/*!
  Returns true, if the HD output was and enabled and HDMI cable is connected.
*/
bool GameWindow::hdConnected() const
{
    return m_hdConnected;
}

/*!
  Returns current frame delta time.
*/
float GameWindow::getFrameTime() const
{
    return m_frameTime;
}

/*!
  Returns current FPS.
*/
float GameWindow::getFPS() const
{
    return m_fps;
}

/*!
  From QWidget.

  GameWindow does not use the paint event for drawing, but the application
  may use the paintevent for drawing onto the display when HD output is
  enabled.
*/
void GameWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
}

/*!
  Return native window handle
*/
EGLNativeWindowType GameWindow::getWindow()
{
    EGLNativeWindowType pwd;

#ifdef Q_OS_SYMBIAN
    if (hdConnected())
        pwd = (EGLNativeWindowType)m_hdmiOutput->hdmiWindow();
    else
        pwd = (EGLNativeWindowType)(winId()->DrawableWindow());
#else // !Q_OS_SYMBIAN
    pwd = (EGLNativeWindowType)winId();
#endif // Q_OS_SYMBIAN

    return pwd;
}

/*!
    Enable/disable HD output. Note that the HD output is not cloned into the
    device's display, so when the HDMI cable is connected nothing is shown on
    the device's display.
*/
void GameWindow::setHdOutput(bool onOff)
{
#ifdef Q_OS_SYMBIAN
    if (onOff) {
        if (!m_hdmiOutput) {
            m_hdmiOutput = new HdmiOutput(this, this);
            QT_TRAP_THROWING(m_hdmiOutput->startMonitoringL())
        }
    }
    else {
        if (m_hdConnected) {
            m_hdConnected = false;
            reinitEGL();
        }
        delete m_hdmiOutput;
        m_hdmiOutput = NULL;
    }
#else // !Q_OS_SYMBIAN
    Q_UNUSED(onOff);
#endif // Q_OS_SYMBIAN
}

/*!
  From QObject.

  Filters \a event sent or posted by \a object. If the event is
  ActivationChanged event, the pause mode is toggled. Otherwise the event is
  propagated.
*/
bool GameWindow::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::ActivationChange) {
        if (isActiveWindow())
            resume();
        else
            pause();

        DEBUG_INFO("ActivationChange event filtered -> toggle pause");
        return false;
    }
#ifdef GE_USE_MM_KEYS
    else if (event->type () == QEvent::KeyPress) {
        QKeyEvent* k = (QKeyEvent*)event;
        switch (k->key()) {
            case Qt::Key_VolumeDown:
                onVolumeDown();
                return true;
            case Qt::Key_VolumeUp:
                onVolumeUp();
                return true;
            default:
                break;
        }
    }
#endif // GE_USE_MM_KEYS

    // Let the event propagate for standard event processing.
    return QObject::eventFilter(object, event);
}

/*!
  From QWidget.

  Returns the width of the widget.
*/
int GameWindow::width()
{
#ifdef Q_OS_SYMBIAN
    if (m_hdConnected)
        return m_hdmiOutput->width();
#endif // Q_OS_SYMBIAN

    return QWidget::width();
}

/*!
  From QWidget.

  Returns the height of the widget.
*/
int GameWindow::height()
{
#ifdef Q_OS_SYMBIAN
    if (m_hdConnected)
        return m_hdmiOutput->height();
#endif // Q_OS_SYMBIAN

    return QWidget::height();
}

/*!
  From QWidget.

  Handles the resize events.
*/
void GameWindow::resizeEvent(QResizeEvent *event)
{
    int w, h;
    if (m_hdConnected) {
        // always fake hdmi output resolution
        w = width();
        h = height();
    }
    else {
        if (event->size().width() == event->oldSize().width() &&
            event->size().height() == event->oldSize().height()) {
            // The size has not changed.
            return;
        }

        w = event->size().width();
        h = event->size().height();
    }

    DEBUG_INFO("Setting the viewport. New size:" << w << "," << h);
    glViewport(0, 0, w, h);
    onSizeChanged(w, h);

    QWidget::resizeEvent(event);
}

/*!
  From QObject.

  Handles the timer events.
*/
void GameWindow::timerEvent(QTimerEvent *event)
{
    Q_UNUSED(event);
    render();
}

/*!
  Called during startup, before OpenGL ES 2.0 is created, to let the
  application to allocate its resources.

  To be implemented in the derived class.
*/
void GameWindow::onCreate()
{
    DEBUG_POINT;
}

/*!
  Called after OpenGL ES 2.0 is created to let the application to allocate
  its EGL resources.

  To be implemented in the derived class.
*/
void GameWindow::onInitEGL()
{
    DEBUG_POINT;
}

/*!
  Called when EGL context is about to be freed to let the application free
  allocated EGL resources.

  To be implemented in the derived class.
*/
void GameWindow::onFreeEGL()
{
    DEBUG_POINT;
}

/*!
  Called when everything is about to be destroyed and the application is
  shutting down.

  To be implemented in the derived class.
*/
void GameWindow::onDestroy()
{
    DEBUG_POINT;
}

/*!
  Called when application needs to (re)render its screen.

  To be implemented in the derived class.
*/
void GameWindow::onRender()
{
    DEBUG_POINT;
}

/*!
  Called when the framework is going into pause mode.

  To be implemented in the derived class.
*/
void GameWindow::onPause()
{
    DEBUG_POINT;
}

/*!
  Called when the framework is resuming from the pause mode.

  To be implemented in the derived class.
*/
void GameWindow::onResume()
{
    DEBUG_POINT;
}

/*!
  Called when volume up -key is pressed.

  Default implementation increases absolute volume by 1/20.
*/
void GameWindow::onVolumeUp()
{
    DEBUG_POINT;
    m_audioMixer.setAbsoluteVolume(
        qMin(1.0f, m_audioMixer.absoluteVolume() + 1.0f / 20.0f));
}

/*!
  Called when volume down -key is pressed.

  Default implementation decreases absolute volume by 1/20.
*/
void GameWindow::onVolumeDown()
{
    DEBUG_POINT;
    m_audioMixer.setAbsoluteVolume(
        qMax(0.0f, m_audioMixer.absoluteVolume() - 1.0f / 20.0f));
}

/*!
  Called once before each frame, \a frameDelta attribute is set as the time
  between current frame and the previous one.

  To be implemented in the derived class.
*/
void GameWindow::onUpdate(const float frameDelta)
{
    Q_UNUSED(frameDelta);
}

/*!
  Called when the size of the screen has been changed. The application could
  update its projection, viewport and other size specific stuff here.

  To be implemented in the derived class.
*/
void GameWindow::onSizeChanged(int width, int height)
{
    Q_UNUSED(width);
    Q_UNUSED(height);
    DEBUG_POINT;
}

/*!
  Create and initialize objects required for OpenGL rendering
*/
void GameWindow::createEGL()
{
    DEBUG_POINT;

    // EGL variables
    eglDisplay	= 0;
    eglConfig	= 0;
    eglSurface	= 0;
    eglContext	= 0;

#ifdef Q_OS_LINUX
    eglDisplay =
        eglGetDisplay((EGLNativeDisplayType)x11Info().display());
#endif // Q_OS_LINUX

#ifdef Q_OS_WIN32
    HWND hwnd = this->winId();
    HDC dc = GetWindowDC(hwnd);
    eglDisplay = eglGetDisplay((EGLNativeDisplayType)dc);
#endif // Q_OS_WIN32

#ifdef Q_OS_SYMBIAN
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
#endif // Q_OS_SYMBIAN

    DEBUG_INFO("eglGetDisplay ==" << eglDisplay);

    EGLint majorVersion;
    EGLint minorVersion;

    if (!eglInitialize(eglDisplay, &majorVersion, &minorVersion)) {
        DEBUG_INFO("Error: eglInitialize() failed!");
        cleanupAndExit(eglDisplay);
    }

    DEBUG_INFO("eglInitialize() finished");

    EGLint pi32ConfigAttribs[13];
    pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
    pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
    pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
    pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;

    pi32ConfigAttribs[4] = EGL_DEPTH_SIZE;
    pi32ConfigAttribs[5] = 8;
    pi32ConfigAttribs[6] = EGL_NONE;

    EGLint pi32ContextAttribs[3];
    pi32ContextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION;
#ifndef GE_USE_OPENGL_ES_1_1
    pi32ContextAttribs[1] = 2;
#else
    pi32ContextAttribs[1] = 1;
#endif
    pi32ContextAttribs[2] = EGL_NONE;

    EGLint configs;

    if (!eglChooseConfig(eglDisplay, pi32ConfigAttribs, &eglConfig, 1, &configs)
            || (configs != 1))  {
        DEBUG_INFO("Error: eglChooseConfig() failed!");
        cleanupAndExit(eglDisplay);
    }

    DEBUG_INFO("eglChooseConfig() finished");

    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, getWindow(), NULL);
    DEBUG_INFO("englCreateWindowSurface() finished");

    if (!testEGLError("eglCreateWindowSurface")) {
        cleanupAndExit(eglDisplay);
    }

    eglContext = eglCreateContext(eglDisplay, eglConfig, NULL,
                                  pi32ContextAttribs);
    DEBUG_INFO("eglCreateContext() finished");

    if (!testEGLError("eglCreateContext")) {
        cleanupAndExit(eglDisplay);
    }

    eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
    DEBUG_INFO("eglMakeCurrent() finished");

    if (!testEGLError("eglMakeCurrent")) {
        cleanupAndExit(eglDisplay);
    }
}

/*!
  Main run - method for the QtGameEnabler, processes a single frame and
  calls necesseary functions of the application using QtGE: update and
  onRender.
*/
void GameWindow::render()
{
    m_prevTime = m_currentTime;
    m_currentTime = getTickCount();
    m_frameTime = (float)(m_currentTime - m_prevTime) * 0.001f;

    if (m_frameTime != 0.0f) {
        m_fps = 1.0f / m_frameTime;
    }
    else {
        m_fps = 100000.0f;
    }

    // Check if a manual tick for audio is required.
    const bool manualTickRequired(m_audioOutput
                                  && m_audioOutput->needsManualTick());

    // 1st audio update
    if (manualTickRequired) {
        m_audioOutput->tick(); // Manual tick
    }

    onUpdate(m_frameTime);

    // 2nd audio update
    if (manualTickRequired) {
        m_audioOutput->tick(); // Manual tick
    }

    onRender();

    if (!eglSwapBuffers(eglDisplay, eglSurface)) {
        // eglSwapBuffers() failed!
        GLint errVal = eglGetError();

        /*
        EXPLANATION:

        http://library.forum.nokia.com/index.jsp?topic=/Nokia_Symbian3_Developers_Library/GUID-894AB487-C127-532D-852B-37CB0DEA1440.html

        On the Symbian platform, EGL handles the window resize in the next
        call to eglSwapBuffers(), which resizes the surface to match the new
        window size. If the preserve buffer option is in use, this function
        also copies across all the pixels from the old surface that overlap
        the new surface, although the exact details depend on the
        implementation.

        If the surface resize fails, eglSwapBuffers() returns EGL_FALSE and an
        EGL_BAD_ALLOC error is raised. This may mean that the implementation
        does not support the resizing of a surface or that there is not enough
        memory available (on a platform with GPU, this would be GPU rather
        than system memory).

        Applications must always monitor whether eglSwapBuffers() fails after
        a window resize. When it does fail, the application should do the
        following:
          - Call eglDestroySurface() to destroy the current EGL window surface.
          - Call eglCreateWindowSurface() to recreate the EGL window surface.

        This may cause a noticeable flicker and so should be done only when
        necessary.
        */

        DEBUG_INFO("eglSwapbuffers() failed with error:" << errVal);

        if (errVal == EGL_BAD_ALLOC || errVal == EGL_BAD_SURFACE) {
            if (errVal == EGL_BAD_ALLOC)
                DEBUG_INFO("Error was bad alloc, taking care of it.");

            eglDestroySurface(eglDisplay, eglSurface);

            eglSurface = eglCreateWindowSurface(eglDisplay,
                                                eglConfig,
                                                getWindow(),
                                                NULL);
            eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
        }
        else {
            cleanupAndExit(eglDisplay);
        }
    }
}

/*!
  Check for EGL errors. \a pszLocation should contain the last called EGL
  function and is used for DEBUG_INFO print. The method returns true in case of no
  errors, false otherwise.
*/
bool GameWindow::testEGLError(const char *pszLocation)
{
#ifndef GE_DEBUG
    Q_UNUSED(pszLocation);
#endif

    EGLint err = eglGetError();

    if (err != EGL_SUCCESS) {
        DEBUG_INFO(pszLocation << "failed" << err);
        return false;
    }

    return true;
}

/*!
  Cleans up the content of \a eglDisplay and exits.
*/
void GameWindow::cleanupAndExit(EGLDisplay eglDisplay)
{
    DEBUG_POINT;

    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroySurface(eglDisplay, eglSurface);

#ifdef Q_OS_SYMBIAN
    if (m_hdmiOutput) {
        m_hdmiOutput->destroyWindow();
    }
#endif // Q_OS_SYMBIAN

    eglTerminate(eglDisplay);
    exit(0);
}

/*!
  Frees and creates the EGL context again. This is called when, for example,
  the underlaying native window changes.
*/
void GameWindow::reinitEGL()
{
    onFreeEGL();

    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroySurface(eglDisplay, eglSurface);

#ifdef Q_OS_SYMBIAN
    if (m_hdmiOutput)
        m_hdmiOutput->destroyWindow();

    if (m_hdConnected)
        QT_TRAP_THROWING(m_hdmiOutput->createWindowL());
#endif // Q_OS_SYMBIAN

    createEGL();
    onInitEGL();
}

#ifndef GE_NOMOBILITY
/*!
  Called the active profile has changed. Audio output is stopped if the device
  has been set to silent profile.
*/
void GameWindow::profileChanged(QSystemDeviceInfo::Profile profile)
{
    if (profile == QSystemDeviceInfo::SilentProfile) {
        stopAudio();
    }
    else {
        if (!m_paused) {
            startAudio();
        }
    }
}
#endif

#ifdef Q_OS_SYMBIAN

/*!
  From IHdmiConnectionObserver.

  Called when HDMI cable has been connected or disconnected.
*/
void GameWindow::hdmiConnected(bool connected)
{
    DEBUG_POINT;
    m_hdConnected = connected;
    reinitEGL();
}

#endif // Q_OS_SYMBIAN

// !! GLHelpers !!
// Not actually part of the official GE, but helps a lot the actual programming.

#ifndef GE_USE_OPENGL_ES_1_1
/*!
  Compile shader program
*/
bool GameWindow::glhelpCompileShader(const QString &message, GLuint &shader)
{
    GLint status;
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);

    if(status == GL_FALSE) {
        DEBUG_INFO(message << "not compiled");

        int logLength, charsWritten;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
        char* infoLog = new char(logLength);
        glGetShaderInfoLog(shader, logLength, &charsWritten, infoLog);
        DEBUG_INFO(infoLog);

        delete [] infoLog;
        return false;

    }
    else{

        DEBUG_INFO(message << "compiled");
        return false;
    }
}

/*!
  Link shader program
*/
bool GameWindow::glhelpLinkProgram(GLuint &program)
{

    GLint status;

    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &status);

    if(status == GL_FALSE){

        DEBUG_INFO("programm not linked!");
        int logLength, charsWritten;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
        if(logLength > 0){
            char* infoLog = new char(logLength);

            glGetProgramInfoLog(program, logLength, &charsWritten, infoLog);

             DEBUG_INFO(infoLog);

            delete [] infoLog;
        }
        return false;
    }else{

        DEBUG_INFO("programm linked");
        return true;
    }
}

/*!
  Load vertex or fragment program code from file
*/
bool GameWindow::glhelpLoadProgram(const QString &source, GLuint &shader)
{
    GLint length;

    QFile file(source);
    if(file.exists()) {
        DEBUG_INFO( source << "found");
        file.open(QIODevice::ReadOnly | QIODevice::Text);

        const char * code = file.readAll();

        glShaderSource(shader, 1, &code, NULL);
        glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &length);

        DEBUG_INFO( "loaded" << length << "characters" );
        file.close();

        return true;
    }
    else {
        DEBUG_INFO( source << " not found");
        return false;
    }
}

/*!
  Swap matrix and set location
*/
void GameWindow::setUniformMatrix(int location, const QMatrix4x4 &matrix)
{
    // we need to flip rows and columns since QMatrix4x4
    // is defined in other-way-around than normal opengl
    float tempMat[4][4];
    for (int r=0; r<4; r++)
    for (int c=0; c<4; c++)
        tempMat[r][c] = matrix.constData()[ r*4 + c ];

    glUniformMatrix4fv(location, 1, GL_FALSE, (float*)&tempMat[0][0]);
}

/*!
  Create vertex and fragment shaders from provided source code. Create a new
  shader for them to be attached. Fills the references with the object handles.
*/
bool GameWindow::glhelpCreateShader(GLuint &vertexShader,
                                    GLuint &fragmentShader,
                                    GLuint &shaderProgram,
                                    const char *vertexShaderSrc,
                                    const char *fragmentShaderSrc)
{
    bool rval(true);
    GLint retval;

    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, (const char**)&fragmentShaderSrc, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &retval);

    if (!retval) {
        DEBUG_INFO("Failed to compile the fragment shader!");
        rval = false;
    }

    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, (const char**)&vertexShaderSrc, NULL);
    glCompileShader(vertexShader);
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &retval);

    if (!retval) {
        DEBUG_INFO("Failed to compile the vertex shader!");
        rval = false;
    }

    shaderProgram = glCreateProgram();

        // Attach the fragment and vertex shaders to it
    glAttachShader(shaderProgram, fragmentShader);
    glAttachShader(shaderProgram, vertexShader);
    glLinkProgram(shaderProgram);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &retval);

    if (!retval) {
        DEBUG_INFO("Error linking the program!");
        rval = false;
    }

    return rval;
}

#endif

/*!
  Loads an image with QImage, converts it to GL's format, creates OpenGL
  texture and returns its handle.
*/
GLuint GameWindow::glhelpLoadTexture(QString imageFile)
{
    QImage image = QImage(imageFile);

    if (image.width() < 1 || image.height() < 1) {
        // Failed to load the texture
        return 0;
    }

    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    GLuint *pTexData = new GLuint[image.width() * image.height()];
    GLuint *sdata = (GLuint*)image.bits();
    GLuint *tdata = pTexData;

    for (int y = 0; y < image.height(); y++) {
        for (int x = 0; x < image.width(); x++) {
            *tdata = ((*sdata&255) << 16) | (((*sdata>>8)&255) << 8)
                    | (((*sdata>>16)&255) << 0) | (((*sdata>>24)&255) << 24);

            sdata++;
            tdata++;
        }
    }

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, pTexData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    delete [] pTexData;
    return texture;
}


code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#ifndef GAMEWINDOW_H
#define GAMEWINDOW_H

#include <QMatrix4x4>

#ifdef Q_WS_MAEMO_5
#include <QApplication>
#include <QMetaType>
#include <QtCore>
#include <QtGui>
#include <QTextStream>
#endif // Q_WS_MAEMO_5

#include <QWidget>
#ifndef GE_NOMOBILITY
#include <QSystemDeviceInfo>
#endif

#include <EGL/egl.h>
#ifdef GE_USE_OPENGL_ES_1_1
#include <GLES2/gl.h>
#else
#include <GLES2/gl2.h>
#endif

#include "geglobal.h"
#include "audiomixer.h"
#include "audioout.h"

#ifdef Q_OS_SYMBIAN
#include "hdmioutput.h"
#include "volumekeys.h"
#endif // Q_OS_SYMBIAN

#ifndef GE_NOMOBILITY
QTM_USE_NAMESPACE
#endif

namespace GE {

class Q_GE_EXPORT GameWindow : public QWidget
#ifdef Q_OS_SYMBIAN
                  ,public IVolumeKeyObserver
                  ,public IHdmiConnectionObserver
#endif // Q_OS_SYMBIAN
{
    Q_OBJECT

public:
    explicit GameWindow(QWidget *parent = 0);
    virtual ~GameWindow();

public:
    void create();
    void destroy();

    bool isProfileSilent() const;
    bool audioEnabled() const;
    AudioMixer &getMixer();

    unsigned int getTickCount() const;
    float getFrameTime() const;
    float getFPS() const;

    void setHdOutput(bool onOff);
    bool hdConnected() const;

    int width();
    int height();

    // GL helper functions.
    GLuint glhelpLoadTexture(QString imageFile);

#ifndef GE_USE_OPENGL_ES_1_1
    bool glhelpCreateShader( GLuint &vertexShader,
                             GLuint &fragmentShader,
                             GLuint &shaderProgram,
                             const char *vertexShaderSrc,
                             const char *fragmentShaderSrc );

    // program manipulation, Originally named as setLocation -> note.
    void setUniformMatrix(int location, const QMatrix4x4 &matrix);

    // code reading, loading, compiling and linking
    bool glhelpLoadProgram(const QString &source, GLuint &shader);
    bool glhelpCompileShader(const QString &message, GLuint &shader);
    bool glhelpLinkProgram(GLuint &program);
#endif

public slots:
    void pause();
    void resume();
    void startAudio();
    void stopAudio();

protected: // Application callbacks, override these in your own derived class
    virtual void onCreate();
    virtual void onInitEGL();
    virtual void onFreeEGL();
    virtual void onDestroy();
    virtual void onRender();
    virtual void onPause();
    virtual void onResume();
    virtual void onUpdate(const float frameDelta);
    virtual void onSizeChanged(int width, int height);
    virtual void onVolumeUp();
    virtual void onVolumeDown();

protected: // From QWidget (and other base classes)
    bool eventFilter(QObject *object, QEvent *event);
    void paintEvent(QPaintEvent *event);
    virtual void resizeEvent(QResizeEvent *event);
    void timerEvent(QTimerEvent *event);

private slots:
#ifndef GE_NOMOBILITY
    void profileChanged(QSystemDeviceInfo::Profile profile);
#endif

private: // For internal functionality
    void createEGL();
    void reinitEGL();
    void render();
    bool testEGLError(const char* pszLocation);
    void cleanupAndExit(EGLDisplay eglDisplay);
    virtual EGLNativeWindowType getWindow();

#ifdef Q_OS_SYMBIAN
private: // From IHdmiConnectionObserver
    void hdmiConnected(bool connected);
#endif

protected: // Data
    // EGL variables
    EGLDisplay eglDisplay;
    EGLConfig eglConfig;
    EGLSurface eglSurface;
    EGLContext eglContext;

private: // Data
    // Time calculation
    unsigned int m_prevTime;
    unsigned int m_currentTime;
    float m_frameTime;
    float m_fps;
    bool m_paused;
    int m_timerId;

    // Audio
    AudioOut *m_audioOutput;
    AudioMixer m_audioMixer;

    // HD output
    bool m_hdConnected;

#ifndef GE_NOMOBILITY
    // For current profile
    QSystemDeviceInfo *m_systemDeviceInfo;
#endif

#ifdef Q_OS_SYMBIAN
    HdmiOutput *m_hdmiOutput;
    VolumeKeys *m_volumeKeys;
#endif // Q_OS_SYMBIAN

};

} // namespace GE

#endif // GAMEWINDOW_H

license: mit


code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#ifndef GEGLOBAL_H
#define GEGLOBAL_H

#include <QtCore/qglobal.h>

#if defined(GE_BUILD_LIB)
    #define Q_GE_EXPORT Q_DECL_EXPORT
#else
    #define Q_GE_EXPORT
#endif

#define Q_GE_BEGIN_NAMESPACE namespace GE {
#define Q_GE_END_NAMESPACE }

#endif // GEGLOBAL_H


code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#include "hdmioutput.h"
#include "trace.h"

_LIT(KWgName, "HdmiOutput");
const TUint32 KWgId = 0xeeeeeeee; // TODO: define unique id
const TUint32 KHdScreen = 1;

using namespace GE;

/*!
  \class HdmiOutput
  \brief Monitors the state of HDMI, and creates a new full-screen window for
         HD output if the HDMI cable is connected.
*/

/*!
  Constructor.
*/
HdmiOutput::HdmiOutput(QObject *parent,
    IHdmiConnectionObserver *observer) :
    QObject(parent),
    m_observer(observer),
    m_accMonitor(NULL),
    m_screenDevice(NULL),
    m_windowGroup(NULL),
    m_window(NULL)
{
    DEBUG_INFO(this);
}

/*!
  Destructor.
*/
HdmiOutput::~HdmiOutput()
{
    DEBUG_POINT;

    delete m_accMonitor;
    destroyWindow();
}

/*!
  Creates the accessory monitor for HDMI.
*/
void HdmiOutput::startMonitoringL()
{
    DEBUG_POINT;

    m_accMonitor = CAccMonitor::NewL();

    RConnectedAccessories array;
    CleanupClosePushL(array);
    m_accMonitor->GetConnectedAccessoriesL(array);

    for (int i = 0; i < array.Count(); i++) {
        if (array[i]->Exists(KAccMonHDMI))
            ConnectedL(array[i]);
    }

    CleanupStack::PopAndDestroy(&array);

    RAccMonCapabilityArray capabilityArray;
    CleanupClosePushL(capabilityArray);
    capabilityArray.Append(KAccMonHDMI);
    m_accMonitor->StartObservingL(this, capabilityArray);
    CleanupStack::PopAndDestroy(&capabilityArray);
}

/*!
  Creates a full-screen window on screen device 1 (HDMI output device).
*/
void HdmiOutput::createWindowL()
{
    DEBUG_POINT;

    if (m_window)
        return;

    m_wsSession.Connect();

    m_screenDevice = new (ELeave) CWsScreenDevice(m_wsSession);
    User::LeaveIfError(m_screenDevice->Construct(KHdScreen));

    m_wsSession.ComputeMode(RWsSession::EPriorityControlDisabled);

    m_windowGroup = new (ELeave) RWindowGroup(m_wsSession);
    User::LeaveIfError(m_windowGroup->Construct(KWgId, m_screenDevice));
    User::LeaveIfError(m_windowGroup->SetName(KWgName));

    m_windowGroup->EnableReceiptOfFocus(EFalse);

    TSize size = m_screenDevice->SizeInPixels();

    m_window = new (ELeave) RWindow(m_wsSession);
    User::LeaveIfError(m_window->Construct(*m_windowGroup, (TUint32)m_window));

    m_window->SetExtent(TPoint(0, 0), size);
    m_window->SetOrdinalPosition(0, ECoeWinPriorityAlwaysAtFront + 1);
    m_window->SetNonFading(ETrue);
    m_window->SetVisible(ETrue);
    m_window->Activate();
}

/*!
  From MAccMonitorObserver

  Called when an accessory device is connected.
*/
void HdmiOutput::ConnectedL(CAccMonitorInfo *accessoryInfo)
{
    DEBUG_POINT;

    if (accessoryInfo->Exists(KAccMonHDMI)) {
        if (m_observer)
            m_observer->hdmiConnected(true);
    }
}

/*!
  From MAccMonitorObserver

  Called when an accessory device is disconnected.
*/
void HdmiOutput::DisconnectedL(CAccMonitorInfo *accessoryInfo)
{
    DEBUG_POINT;

    if (accessoryInfo->Exists(KAccMonHDMI)) {
        if (m_observer)
            m_observer->hdmiConnected(false);
    }
}

/*!
  From MAccMonitorObserver

  Called when an error occured. Intentionally empty, since hdmiConnected
  callback should not leave.
*/
void HdmiOutput::AccMonitorObserverError(TInt error)
{
    DEBUG_INFO("AccMonitorObserverError:" << error);
}

/*!
  Destroys the HD output window.
*/
void HdmiOutput::destroyWindow()
{
    DEBUG_POINT;

    if (!m_window)
        return;

    m_window->Close();
    delete m_window;
    m_window = NULL;

    m_windowGroup->Close();
    delete m_windowGroup;
    m_windowGroup = NULL;

    m_wsSession.Close();
}

/*!
  Getter for the HD output window.
*/
RWindow *HdmiOutput::hdmiWindow()
{
    DEBUG_POINT;
    return m_window;
}

/*!
  Return the HD output window width, or zero if the window is not created.
*/
int HdmiOutput::width()
{
    DEBUG_POINT;
    return m_screenDevice ? m_screenDevice->SizeInPixels().iWidth : 0;
}

/*!
  Return the HD output window height, or zero if the window is not created.
*/
int HdmiOutput::height()
{
    DEBUG_POINT;
    return m_screenDevice ? m_screenDevice->SizeInPixels().iHeight : 0;
}


code:
/**
 * Copyright (c) 2011 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#ifndef HDMIOUTPUT_H
#define HDMIOUTPUT_H

#include <QtGui>
#include <gcc_mingw/AccMonitor.h>

namespace GE {

class IHdmiConnectionObserver
{
public:
    virtual void hdmiConnected(bool connected) = 0;
};

class HdmiOutput : public QObject,
                   public MAccMonitorObserver
{
    Q_OBJECT

public:
    explicit HdmiOutput(QObject *parent, IHdmiConnectionObserver *observer);
    ~HdmiOutput();

public:
    void startMonitoringL();
    void stopMonitoring();

    void createWindowL();
    void destroyWindow();
    RWindow *hdmiWindow();

    int width();
    int height();

private: // From MAccMonitorObserver
    virtual void ConnectedL(CAccMonitorInfo *accessoryInfo);
    virtual void DisconnectedL(CAccMonitorInfo *accessoryInfo);
    virtual void AccMonitorObserverError(TInt error);

private:
    IHdmiConnectionObserver *m_observer;
    CAccMonitor *m_accMonitor;
    RWsSession m_wsSession;
    CWsScreenDevice *m_screenDevice;
    RWindowGroup *m_windowGroup;
    RWindow *m_window;
};

} // namespace GE

#endif // HDMIOUTPUT_H


code:
/**
 * Copyright (c) 2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#ifndef OGGCONSTANTS_H
#define OGGCONSTANTS_H

namespace GE {

// SggO = OggS
const unsigned int OggCapturePatternMagic = 0x5367674F;

// See Vorbis I specification
// Xiph.Org Foundation
// August 11, 2011

// 'The first packet on this page is a continuation of the previous packet
// in the logical bitstream.'
const unsigned char OggContinuation = 0x01;

// 'Beginning Of Stream. This page is the first page in the logical
// bitstream. The BOS flag must be set on the first page of every logical
// bitstream, and must not be set on any other page.'
const unsigned char OggBOS = 0x02;

// 'End Of Stream. This page is the last page in the logical bitstream. The EOS
// flag must be set on the final page of every logical bitstream, and must not
// be set on any other page.'
const unsigned char OggEOS = 0x04;

// Length of the OGG header from capture pattern to page segments
const unsigned int OggPageHeaderLen = 27;

// 'The first Vorbis packet (the identification header), which uniquely
// identifies a stream as Vorbis audio, is placed alone in the first page of the
// logical Ogg stream. This results in a first Ogg page of exactly 58 bytes at
// the very beginning of the logical stream.'
const unsigned int OggFirstPageLen = 58;

}

#endif // OGGCONSTANTS_H


code:
/**
 * Copyright (c) 2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#include <memory.h>
#include <stdlib.h>

#include "trace.h"
#include "oggpage.h"
#include "oggconstants.h"

using namespace GE;

OggPage::OggPage()
{
    memset(this, 0, sizeof(OggPage));
}

OggPage::~OggPage()
{
    free(m_segments);
}

void OggPage::dump() const
{
    DEBUG_INFO("***");
    DEBUG_INFO("Page" << this);
    DEBUG_INFO("    capturePattern:" << m_capturePattern);
    DEBUG_INFO("           version:" << m_version);
    DEBUG_INFO("        headerType:" << m_headerType << "(" <<
        (m_headerType & OggContinuation ? " Continuation " : "") <<
        (m_headerType & OggBOS ? " BOS " : "") <<
        (m_headerType & OggEOS ? " EOS " : "") << ")"
     );
    DEBUG_INFO("   granulePosition:" << m_granulePosition);
    DEBUG_INFO("bitstreamSerialNum:" << m_bitstreamSerialNum);
    DEBUG_INFO("   pageSequenceNum:" << m_pageSequenceNum);
    DEBUG_INFO("          checksum:" << m_checksum);
    DEBUG_INFO("      pageSegments:" << m_pageSegments);
    DEBUG_INFO("      pageStartPos:" << m_pageStartPos);
    DEBUG_INFO("        dataLength:" << m_dataLength);
    DEBUG_INFO("      dataStartPos:" << m_dataStartPos);

    if (!m_segments) {
        DEBUG_INFO("No segments");
        return;
    }

    for (int t = 0; t < m_pageSegments; t++) {
        DEBUG_INFO("          Segments:" << t << "," << m_segments[t]);
    }
}


code:
/**
 * Copyright (c) 2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#ifndef GE_OGGPAGE_H
#define GE_OGGPAGE_H

#include <QtGlobal>

namespace GE {

#pragma pack(1)
class OggPage
{
public:
    OggPage();
    ~OggPage();

    void dump() const;

public:
    // OGG page header
    unsigned int m_capturePattern;
    unsigned char m_version;
    unsigned char m_headerType;
    quint64 m_granulePosition;
    unsigned int m_bitstreamSerialNum;
    unsigned int m_pageSequenceNum;
    unsigned int m_checksum;
    unsigned char m_pageSegments;
    unsigned char *m_segments;

    // Calculated stuff

    // Starting position of the page in the ogg file
    unsigned int m_pageStartPos;

    // Length of the page
    unsigned int m_pageLength;

    // Length of the page excluding the header
    unsigned int m_dataLength;

    // Starting position of the data in the ogg file
    unsigned int m_dataStartPos;

    OggPage *m_next;
    OggPage *m_prev;
};

#pragma pack()

} // namespace GE

#endif // GE_OGGPAGE_H


code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#include "pullaudioout.h"
#include <qglobal.h>
#include "trace.h" // For debug macros

#if defined(QTGAMEENABLER_USE_VOLUME_HACK) && defined(Q_OS_SYMBIAN)
    #include <sounddevice.h>
    #include <QSystemInfo>
    QTM_USE_NAMESPACE
#endif

using namespace GE;

/*!
  \class PullAudioOut
  \brief An object deploying QAudioOutput for sending the pre-mixed/processed
         audio data into an actual audio device. Uses QAudioOutput's pull mode.
*/

/*!
  Constructor.
*/
PullAudioOut::PullAudioOut(AudioSource *source, QObject *parent)
    : QIODevice(parent),
      m_source(source)
{
    DEBUG_INFO(this);

    QAudioFormat format;
    format.setFrequency(AUDIO_FREQUENCY);
    format.setChannels(AUDIO_CHANNELS);
    format.setSampleSize(AUDIO_SAMPLE_BITS);
    format.setCodec(GEDefaultAudioCodec);
    format.setByteOrder(GEByteOrder);
    format.setSampleType(GESampleType);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());

    if (!info.isFormatSupported(format))
        format = info.nearestFormat(format);

    m_sendBufferSize = 4096;
    m_audioOutput = new QAudioOutput(info, format);

    open(QIODevice::ReadOnly | QIODevice::Unbuffered);
    connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)),
        SLOT(audioStateChanged(QAudio::State)));
    m_audioOutput->start(this);

#if defined(QTGAMEENABLER_USE_VOLUME_HACK) && defined(Q_OS_SYMBIAN)
    DEBUG_INFO("WARNING: Using the volume hack!");

    // This really ugly hack is used as the last resort. This allows us to
    // adjust the application volume in Symbian. The CMMFDevSound object lies
    // deep inside the QAudioOutput in Symbian implementation and it has the
    // needed functions. So, we get the needed object accessing it directly
    // from memory.
    unsigned int *pointer_to_abstract_audio =
            (unsigned int*)((unsigned char*)m_audioOutput + 8);

    unsigned int *dev_sound_wrapper = NULL;
    QSystemInfo sysInfo;
    QString mobilityVersion(sysInfo.version(QSystemInfo::QtMobility));

    if (mobilityVersion == QLatin1String("1.2.0") ||
        mobilityVersion == QLatin1String("1.2.1")) {
        dev_sound_wrapper = (unsigned int*)(*pointer_to_abstract_audio) + 16;
    } else if (mobilityVersion >= QLatin1String("1.2.2")) {
        // Do not use the volume hack with 1.2.2 and newer
        dev_sound_wrapper = NULL;
    } else {
        // Older than 1.2.0
        dev_sound_wrapper = (unsigned int*)(*pointer_to_abstract_audio) + 13;
    }

    if (dev_sound_wrapper) {
        unsigned int *temp = ((unsigned int*)(*dev_sound_wrapper) + 6);
        CMMFDevSound *devSound = (CMMFDevSound*)(*temp);
        devSound->SetVolume(devSound->MaxVolume() * 6 / 10);
    }
#endif
}

/*!
  Destructor.
*/
PullAudioOut::~PullAudioOut()
{
    m_audioOutput->stop();

    delete m_audioOutput;
    m_audioOutput = 0;

    close();
}

void PullAudioOut::audioStateChanged(QAudio::State state)
{
    Q_UNUSED(state);
    DEBUG_INFO("AudioStateChanged:" << state <<
        " error:" << m_audioOutput->error());

    if (m_audioOutput->error() == QAudio::UnderrunError)
        m_audioOutput->start(this);
}

/*!
  From QIODevice

  Returns the number of bytes that are available for reading.
*/
qint64 PullAudioOut::bytesAvailable() const
{
    return (qint64)m_sendBufferSize * sizeof(AUDIO_SAMPLE_TYPE);
}

/*!
  From QIODevice

  Writes up to maxSize bytes from data to the device. Since this is a ReadOnly
  device the implementation does nothing.
*/
qint64 PullAudioOut::writeData(const char *data, qint64 len)
{
    Q_UNUSED(data);
    Q_UNUSED(len);
    return 0;
}

/*!
  From QIODevice

  Reads up to maxSize bytes from the device into data.
*/
qint64 PullAudioOut::readData(char *data, qint64 maxlen)
{
    if (m_source.isNull()) {
        DEBUG_INFO("No audio source!");
        return -1;
    }

    int sampleCount = maxlen / 2;  // How many samples we mix maximumly.
    sampleCount /= 4;              // The partialbuffer upgrade part.

    if (sampleCount > m_sendBufferSize)
        sampleCount = m_sendBufferSize;

    // QAudioOutput does not use period size on Symbian, and periodSize
    // returns always the bufferSize.
#ifndef Q_OS_SYMBIAN
    // Use multiples of period size
    sampleCount = sampleCount * sizeof(AUDIO_SAMPLE_TYPE) /
        m_audioOutput->periodSize();
    if (sampleCount)
        sampleCount *= m_audioOutput->periodSize();
    else
        sampleCount = qMin(maxlen, (qint64)m_audioOutput->periodSize());
    sampleCount /= sizeof(AUDIO_SAMPLE_TYPE);
#endif // !Q_OS_SYMBIAN

    memset(data, 0, sampleCount * sizeof(AUDIO_SAMPLE_TYPE));
    int mixedSamples = m_source->pullAudio((AUDIO_SAMPLE_TYPE*)data,
        sampleCount);

    if (mixedSamples < sampleCount)
        mixedSamples = sampleCount;

    return (qint64)mixedSamples * sizeof(AUDIO_SAMPLE_TYPE);
}


code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#ifndef GEPULLAUDIOOUT_H
#define GEPULLAUDIOOUT_H

#include <QAudioOutput>
#include <QIODevice>
#include <QPointer>
#include "geglobal.h"
#include "audioout.h"

namespace GE {

/**
 *
 * Pull  mode solution
 *
 */
class Q_GE_EXPORT PullAudioOut : public QIODevice,
                     public AudioOut
{
    Q_OBJECT

public:
    PullAudioOut(AudioSource *source, QObject *parent = 0);
    virtual ~PullAudioOut();

public: // from QIODevice
    qint64 bytesAvailable() const;
    qint64 readData(char *data, qint64 maxlen);
    qint64 writeData(const char *data, qint64 len);

public slots:
    void audioStateChanged(QAudio::State state);

protected:
    QAudioOutput* m_audioOutput;        // Owned
    int m_sendBufferSize;
    QPointer<AudioSource> m_source; // Not owned
};

} // namespace GE

#endif // GEPULLAUDIOOUT_H


code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#include "pushaudioout.h"
#include "trace.h" // For debug macros

#if defined(QTGAMEENABLER_USE_VOLUME_HACK) && defined(Q_OS_SYMBIAN)
    #include <sounddevice.h>
    #include <QSystemInfo>
    QTM_USE_NAMESPACE
#endif

// Constants
const int GEThreadSleepTime(1); // Milliseconds

using namespace GE;

/*!
  \class PushAudioOut
  \brief An object deploying QAudioOutput for sending the pre-mixed/processed
         audio data into an actual audio device.
*/

/*!
  Constructor.
*/
PushAudioOut::PushAudioOut(AudioSource *source, QObject *parent /* = 0 */)
    : QThread(parent),
      m_audioOutput(0),
      m_outTarget(0),
      m_sendBuffer(0),
      m_sendBufferSize(0),
      m_samplesMixed(0),
      m_threadState(NotRunning),
      m_source(source)
{
    DEBUG_INFO(this);

    QAudioFormat format;
    format.setFrequency(AUDIO_FREQUENCY);
    format.setChannels(AUDIO_CHANNELS);
    format.setSampleSize(AUDIO_SAMPLE_BITS);
    format.setCodec(GEDefaultAudioCodec);
    format.setByteOrder(GEByteOrder);
    format.setSampleType(GESampleType);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());

    if (!info.isFormatSupported(format))
        format = info.nearestFormat(format);

    m_audioOutput = new QAudioOutput(info, format);

#if defined(Q_WS_MAEMO_5) || defined(MEEGO_EDITION_HARMATTAN)
    m_audioOutput->setBufferSize(4096 * 8);
    m_sendBufferSize = 4096 * 4;
#else
    m_audioOutput->setBufferSize(4096 * 4);
    m_sendBufferSize = 4096 * 2;
#endif

    m_outTarget = m_audioOutput->start();

    DEBUG_INFO("Buffer size: " << m_audioOutput->bufferSize());
    m_sendBuffer = new AUDIO_SAMPLE_TYPE[m_sendBufferSize];

#ifdef Q_OS_SYMBIAN
    m_needsTick = true;

#if defined(QTGAMEENABLER_USE_VOLUME_HACK)
    DEBUG_INFO("WARNING: Using the volume hack!");

    // This really ugly hack is used as the last resort. This allows us to
    // adjust the application volume in Symbian. The CMMFDevSound object lies
    // deep inside the QAudioOutput in Symbian implementation and it has the
    // needed functions. So, we get the needed object accessing it directly
    // from memory.
    unsigned int *pointer_to_abstract_audio =
            (unsigned int*)((unsigned char*)m_audioOutput + 8);

    unsigned int *dev_sound_wrapper = NULL;
    QSystemInfo sysInfo;
    QString mobilityVersion(sysInfo.version(QSystemInfo::QtMobility));

    if (mobilityVersion == QLatin1String("1.2.0") ||
        mobilityVersion == QLatin1String("1.2.1")) {
        dev_sound_wrapper = (unsigned int*)(*pointer_to_abstract_audio) + 16;
    } else if (mobilityVersion >= QLatin1String("1.2.2")) {
        // Do not use the volume hack with 1.2.2 and newer
        dev_sound_wrapper = NULL;
    } else {
        // Older than 1.2.0
        dev_sound_wrapper = (unsigned int*)(*pointer_to_abstract_audio) + 13;
    }

    if (dev_sound_wrapper) {
        unsigned int *temp = ((unsigned int*)(*dev_sound_wrapper) + 6);
        CMMFDevSound *devSound = (CMMFDevSound*)(*temp);
        devSound->SetVolume(devSound->MaxVolume() * 6 / 10);
    }
#endif

#else // !Q_OS_SYMBIAN
    m_needsTick = false;
    start();
#endif // Q_OS_SYMBIAN
}

/*!
  Destructor.
*/
PushAudioOut::~PushAudioOut()
{
    if (m_threadState == DoRun) {
        // Set the thread to exit run().
        m_threadState = DoExit;
    }

    if (QThread::isRunning() == false) {
        m_threadState = NotRunning;
    }

    while (m_threadState != NotRunning) {
        // Wait until the thread is finished.
        msleep(50);
    }

    m_audioOutput->stop();

    delete m_audioOutput;
    delete [] m_sendBuffer;
}

/*!
  Call this method manually only if you are not using a thread (with Symbian).

  Note: When using Qt GameEnabler, the GameWindow instance owning this AudioOut
  instance will handle calling this method and you should not try to call this
  explicitly.
*/
void PushAudioOut::tick()
{
    if (m_source.isNull()) {
        DEBUG_INFO("No audio source!");
        return;
    }

    // QAudioOutput does not use period size on Symbian, and periodSize
    // returns always the bufferSize
#ifdef Q_OS_SYMBIAN
    // Fill data to the buffer as much as there is free space available.
    int samplesToWrite(m_audioOutput->bytesFree() / sizeof(AUDIO_SAMPLE_TYPE));
    if (samplesToWrite <= 0)
        return;
#else // !Q_OS_SYMBIAN
    // Use multiples of period size
    int samplesToWrite(m_audioOutput->bytesFree() / m_audioOutput->periodSize());
    if (!samplesToWrite)
        return;
    samplesToWrite *= m_audioOutput->periodSize() / sizeof(AUDIO_SAMPLE_TYPE);
#endif // Q_OS_SYMBIAN

    if (samplesToWrite > m_sendBufferSize)
        samplesToWrite = m_sendBufferSize;

    int mixedSamples = m_source->pullAudio(m_sendBuffer, samplesToWrite);
    m_outTarget->write((char*)m_sendBuffer,
        mixedSamples * sizeof(AUDIO_SAMPLE_TYPE));
}

/*!
  From QThread.

  Used only in threaded solutions.
*/
void PushAudioOut::run()
{
    DEBUG_INFO("Starting thread.");
    m_threadState = DoRun;

    if (m_source.isNull()) {
        DEBUG_INFO("No audio source, exiting the thread!");
        m_threadState = NotRunning;
        return;
    }

    while (m_threadState == DoRun) {
        tick();
        msleep(GEThreadSleepTime);
    }

    DEBUG_INFO("Exiting thread.");
    m_threadState = NotRunning;
}


code:
/**
 * Copyright (c) 2011-2012 Nokia Corporation.
 * All rights reserved.
 *
 * Part of the Qt GameEnabler.
 *
 * For the applicable distribution terms see the license text file included in
 * the distribution.
 */

#ifndef GEPUSHAUDIOOUT_H
#define GEPUSHAUDIOOUT_H

#include <QAudioOutput>
#include <QThread>
#include <QIODevice>
#include <QPointer>
#include "geglobal.h"
#include "audioout.h"

namespace GE {

class Q_GE_EXPORT PushAudioOut : public QThread,
                                 public AudioOut
{
    Q_OBJECT

public:
    PushAudioOut(AudioSource *source, QObject *parent = 0);
    virtual ~PushAudioOut();

public:
    bool needsManualTick() const { return m_needsTick; }
    void tick();

protected: // From QThread
     virtual void run(); // For the threaded mode only!

private: // Data types
    enum ThreadStates {
        NotRunning = 0,
        DoRun = 1,
        DoExit = 2
    };

protected: // Data
    QAudioOutput *m_audioOutput; // Owned
    QIODevice *m_outTarget; // Not owned
    AUDIO_SAMPLE_TYPE *m_sendBuffer; // Owned
    int m_sendBufferSize;
    qint64 m_samplesMixed;
    int m_threadState;
    bool m_needsTick;
    QPointer<AudioSource> m_source; // Not owned
};

} // namespace GE

#endif // GEPUSHAUDIOOUT_H


code:
# Copyright (c) 2011-2012 Nokia Corporation.

TEMPLATE = lib
TARGET = QtGameEnabler$${QT_LIBINFIX}
MOC_DIR = .moc
OBJECTS_DIR = .obj
DESTDIR = .lib
CONFIG += create_prl
DEFINES += GE_BUILD_LIB

contains(QT_CONFIG, reduce_exports): CONFIG += hide_symbols

GE_PATH = ../
include(../qtgameenabler.pri)

PUBLIC_HEADERS = $$HEADERS
HEADERS += $$PRIVATE_HEADERS

feature.path = $$[QT_INSTALL_DATA]/mkspecs/features
feature.files = ../features/qtgameenabler.prf
INSTALLS += feature

win32 {
    DLLDESTDIR = ../bin
    !static:DEFINES += QT_MAKEDLL

    CONFIG(debug, debug|release) {
        TARGET = $$member(TARGET, 0)d
    }
}

symbian {
    DEFINES += QT_MAKEDLL
    CONFIG += epocallowdlldata
    contains(QT_EDITION, OpenSource) {
        TARGET.CAPABILITY = LocalServices ReadUserData UserEnvironment WriteUserData
    } else {
        TARGET.CAPABILITY = All -Tcb
    }

    load(data_caging_paths)

    isEmpty(QT_LIBINFIX) {
        # TODO: signed UID
        TARGET.UID3 = 0xE005B322
    } else {
        TARGET.UID3 = 0xE005B322
    }

    qtgameenablerlibraries.sources = QtGameEnabler$${QT_LIBINFIX}.dll
    qtgameenablerlibraries.path = /sys/bin
    DEPLOYMENT += qtgameenablerlibraries
} else {
    target.path += $$[QT_INSTALL_LIBS]
    INSTALLS += target

    install_headers.path = $$[QT_INSTALL_HEADERS]/$$TARGET
    install_headers.files = $$PUBLIC_HEADERS
    INSTALLS += install_headers
}

# End of file.