Create and render the cube

First, we need to add a new class to our project:

  1. In your project, right-click the src folder and click New > Class.
  2. Clear the Namespace check box if it is selected.
  3. Type MyView as the new class name.
  4. Click Finish.

Declare our functions and variables

In MyView.h, let's start by adding the following libraries after we define MYVIEW_H_:

#include <assert.h>
#include <screen/screen.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <bps/navigator.h>
#include <bps/screen.h>
#include <bps/bps.h>
#include <bps/event.h>

#include <EGL/egl.h>
#include <GLES/gl.h>

#include <bb/cascades/TouchEvent>

#include <QtCore/QObject>
#include <QtCore/QString>

#include "OpenGLView.hpp"
#include "OpenGLThread.hpp"

Our class declaration inherits from OpenGLView to obtain all of the necessary functionality that the OpenGLView class provides.

class MyView : public OpenGLView {

	Q_OBJECT

We declare public functions that override abstract functions from OpenGLView. If you're familiar with OpenGL ES, you will recognize the naming standards of these functions. For example, the initialize() function initializes the OpenGL ES model and pipeline for rendering, which is standard in most OpenGL ES apps.

public:
	MyView(VIEW_DISPLAY display);
	virtual ~MyView();

	int initialize();
	int regenerate();
	void cleanup();

	void handleScreenEvent(bps_event_t *event);

	void update();
	void render();

Lastly, we declare two float arrays for our vertices and colors.

private:

    static float vertices[];
    static float colors[];

};

Define the cube

In MyView.cpp, clear out the contents of the entire stub that was generated for your new class. We start our class implementation by defining the libraries that we use and including the geometry and colors for your OpenGL ES model. We're defining a cube, and OpenGL ES operates on triangles. Therefore, we have two triangles per side which equals six vertices. In 3-D space we have three coordinates, so six vertices times three is eighteen values per side.

For colors, we're using an RGBA color space, and we define a color for each side.

#include "MyView.h"
#include <math.h>

#include <QDebug>
#include <QFile>
using namespace bb::cascades;

float MyView::vertices[] = {
      // front
      -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
       0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f,

      // right
      0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
      0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f,

      // back
      0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f,
      -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,

      // left
      -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, 0.5f,
      -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f,

      // top
      -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
       0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,

      // bottom
      -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f,
       0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f
};

float MyView::colors[] = {
       /* front  */  0.0625f,     0.57421875f, 0.92578125f, 
				     1.0f,        0.0625f,     0.57421875f,
				     0.92578125f, 1.0f,        0.0625f,
                     0.57421875f, 0.92578125f, 1.0f,
                     0.0625f,     0.57421875f, 0.92578125f,
                     1.0f, 	   0.0625f,     0.57421875f,
                     0.92578125f, 1.0f,        0.0625f,
					 0.57421875f, 0.92578125f, 1.0f,

       /* right  */  0.29296875f, 0.66796875f, 0.92578125f,
					 1.0f,        0.29296875f, 0.66796875f,
				     0.92578125f, 1.0f,        0.29296875f,
                     0.66796875f, 0.92578125f, 1.0f,
                     0.29296875f, 0.66796875f, 0.92578125f,
                     1.0f,        0.29296875f, 0.66796875f,
                     0.92578125f, 1.0f,        0.29296875f,
                     0.66796875f, 0.92578125f, 1.0f,

       /* back   */  0.52734375f, 0.76171875f, 0.92578125f,
					 1.0f,        0.52734375f, 0.76171875f,
					 0.92578125f, 1.0f,        0.52734375f,
					 0.76171875f, 0.92578125f, 1.0f,
					 0.52734375f, 0.76171875f, 0.92578125f,
                     1.0f,        0.52734375f, 0.76171875f,
					 0.92578125f, 1.0f,        0.52734375f,
					 0.76171875f, 0.92578125f, 1.0f,

       /* left   */  0.0625f,     0.57421875f, 0.92578125f,
				     1.0f,        0.0625f,     0.57421875f,
					 0.92578125f, 1.0f,        0.0625f,
					 0.57421875f, 0.92578125f, 1.0f,
					 0.0625f,     0.57421875f, 0.92578125f,
                     1.0f,        0.0625f,     0.57421875f,
					 0.92578125f, 1.0f,        0.0625f,
					 0.57421875f, 0.92578125f, 1.0f,

       /* top    */  0.29296875f, 0.66796875f, 0.92578125f,
					 1.0f,        0.29296875f, 0.66796875f,
                     0.92578125f, 1.0f,        0.29296875f,
					 0.66796875f, 0.92578125f, 1.0f,
					 0.29296875f, 0.66796875f, 0.92578125f,
					 1.0f,        0.29296875f, 0.66796875f,
                     0.92578125f, 1.0f,        0.29296875f,
                     0.66796875f, 0.92578125f, 1.0f,

       /* bottom */  0.52734375f, 0.76171875f, 0.92578125f,
					 1.0f,        0.52734375f, 0.76171875f,
					 0.92578125f, 1.0f,        0.52734375f,
					 0.76171875f, 0.92578125f, 1.0f,
					 0.52734375f, 0.76171875f, 0.92578125f,
					 1.0f,        0.52734375f, 0.76171875f,
					 0.92578125f, 1.0f,        0.52734375f,
					 0.76171875f, 0.92578125f, 1.0f
};

In the constructor, set the display type based on the parameter. The destructor and update() functions don't contain any code but they're required to conform to rules for abstract functions.

MyView::MyView(VIEW_DISPLAY display) {
	setDisplay(display);
}

MyView::~MyView() {
	// TODO Auto-generated destructor stub
}
void MyView::update() {

}

The cleanup() and regenerate() functions call their corresponding functions from OpenGLView.

void MyView::cleanup(){
	OpenGLView::cleanup();
}

int MyView::regenerate()
{
	int returnCode = OpenGLView::regenerate();
	return returnCode;
}

Handle a Screen event

Now we implement a handleScreenEvent() function to handle Screen events. For this example, we won't do much with Screen events, except show how to respond to them and print out some console messages.

void MyView::handleScreenEvent(bps_event_t *event) {
    int screenEvent;
    int buttons;
    int position[2];

    screen_event_t screen_event = screen_event_get_event(event);

    //Query type of screen event and its location on the screen
    screen_get_event_property_iv(screen_event, 
								SCREEN_PROPERTY_TYPE,
            					&screenEvent);

    screen_get_event_property_iv(screen_event, 
								SCREEN_PROPERTY_SOURCE_POSITION,
    							position);

	switch (screenEvent) {
		case SCREEN_EVENT_MTOUCH_TOUCH:
			break;

		case SCREEN_EVENT_MTOUCH_MOVE:
			break;

		case SCREEN_EVENT_MTOUCH_RELEASE:
			break;

		case SCREEN_EVENT_POINTER:
	        screen_get_event_property_iv(screen_event,
										SCREEN_PROPERTY_BUTTONS,
										&buttons);

	    	switch (buttons) {
	    		case SCREEN_LEFT_MOUSE_BUTTON:
	    		case SCREEN_RIGHT_MOUSE_BUTTON:
	                //handleClick(position[0], position[1]);
	    			break;
	    	}

			break;
	}
}

Initialize the model

Now we write the initialization code for our OpenGL ES model. In the initialize() function, we set the Z depth to -5 and call the initGL() function from OpenGLView. If there are no errors, we'll set the stale state to true and initialize the model.

Next, we obtain the EGL surface width and height. We set the image depth value by calling glClearDepthf(). We also enable smooth shading of the model and enable polygons to be culled based on their window coordinates. We call glClearColor() to set the clear color to black, so that when we call glClear(GL_COLOR_BUFFER_BIT) , the screen is cleared to this color. Then, we specify the size of the viewport through which we view the scene. We use glViewport() to set the viewport to the same size as the EGL surface. Some of this may differ from the template, so verify that your code is exactly the same as is shown here.

int MyView::initialize(){

	setZ(-5);

	int returnCode = OpenGLView::initGL();
	if (returnCode == EXIT_SUCCESS) {

		setStale(true);
        EGLint surface_width, surface_height;

        eglQuerySurface(m_egl_disp,
						m_egl_surf,
						EGL_WIDTH, 
						&surface_width);

        eglQuerySurface(m_egl_disp,
						m_egl_surf, 
						EGL_HEIGHT, 
						&surface_height);

        EGLint err = eglGetError();
        if (err != 0x3000) {
            fprintf(stderr, 
				"Unable to query EGL surface dimensions\n");
            return EXIT_FAILURE;
        }

        glClearDepthf(1.0f);
        glEnable(GL_CULL_FACE);
        glShadeModel(GL_SMOOTH);

        //set clear color to black
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        glViewport(0, 0, surface_width, surface_height);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

To specify the projection matrix we need to do a bit of trigonometry. We specify the near and far clipping planes, the vertical field of view, and the aspect ratio. Then we calculate the minimum and maximum horizontal and vertical values. We call glFrustumf() to perform a perspective projection. Next, we scale the model, but adjust the x- and y-axes for scaling, depending on the surface width and height. Finally, we set the current matrix to modelview, and load the identity matrix.

        float nearClip = -2.0f;
        float farClip  = 2.0f;
        float yFOV  = 75.0f;
        float yMax = nearClip * tan(yFOV*M_PI/360.0f);
        float aspect = surface_width/surface_height;
        float xMin = -yMax * aspect;
        float xMax = yMax *aspect;

        glFrustumf(xMin, xMax, -yMax, yMax, nearClip, farClip);

        if (surface_width > surface_height){
            glScalef(
			(float)surface_height/(float)surface_width,
			1.0, 1.0f);
        } else {
            glScalef(1.0, 
			(float)surface_width/(float)surface_height, 
			1.0f);
        } 
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
	}

    return returnCode;
}

Render the cube

We need a function to update the OpenGL ES model on a per frame basis. Our render() function can be used to do this for us. To start things off, we set the stale state to false and use glClear() to clear the color and depth buffers. Next, we enable the passing of a vertex array to describe the geometry of our cube and a color array to specify the colors used on the sides of the cube. We specify the arrays of vertices and color values that we defined at the beginning of the tutorial to be used to render the model.

void MyView::render(){

	setStale(false);

	glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glEnableClientState ( GL_VERTEX_ARRAY );
    glVertexPointer ( 3 , GL_FLOAT , 0 , vertices );

    glEnableClientState ( GL_COLOR_ARRAY );
    glColorPointer ( 4 , GL_FLOAT , 0 , colors );

Next, we call glRotatef() to rotate the cube, and call glDrawArrays() to draw the triangles. Remember that our representation requires 36 vertices because we specified the geometric shape as a set of triangle strips. We disable vertex and color array states, then set the stale state to true.

	glRotatef ( 1.0f , 1.0f , 1.0f , 0.0f );
    glDrawArrays ( GL_TRIANGLES , 0 , 36 );
    glDisableClientState ( GL_VERTEX_ARRAY );
    glDisableClientState ( GL_COLOR_ARRAY );

	setStale(true);
}

And that's it. You can deploy and run the app to test it yourself.

Complete source code

import bb.cascades 1.0

NavigationPane {
    Page {
        content: Container {
            id: back
            
            objectName: "back"           
            background: Color.create("#262626")
            horizontalAlignment: HorizontalAlignment.Fill
            verticalAlignment: VerticalAlignment.Fill
            
            layout: StackLayout {
                orientation: LayoutOrientation.TopToBottom
            }
            
            ForeignWindowControl {
                id: foreignWindow
                objectName: "myForeignWindow"
                visible: true
                
                horizontalAlignment: HorizontalAlignment.Fill
                verticalAlignment: VerticalAlignment.Fill
            }       
        }
    }    
}
#include <bb/cascades/AbsoluteLayout>
#include <bb/cascades/StackLayoutProperties>
#include <bb/cascades/Application>
#include <bb/cascades/Window>
#include <bb/cascades/Button>
#include <bb/cascades/Container>
#include <bb/cascades/Color>
#include <bb/cascades/ForeignWindowControl>
#include <bb/cascades/ImplicitAnimationController>
#include <bb/cascades/Page>
#include <bb/cascades/LayoutUpdateHandler>
#include <bb/cascades/NavigationPane>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/SceneCover>

#include <QtCore/QTimer>
#include <QDebug>

#include "applicationui.hpp"

#include <screen/screen.h>

#include <GLES/gl.h>
#include <GLES/glext.h>
#include <EGL/egl.h>
#include <sys/strm.h>

using namespace bb::cascades;

ApplicationUI::ApplicationUI
	(bb::cascades::Application *app) :QObject(app)
{
    // Connect the manualExit() signal to the
    // shutdown() slot, which closes the app manually.

    bool result;
    Q_UNUSED(result);

	result = QObject::connect(
		Application::instance(),
		SIGNAL(manualExit()),
		this,
		SLOT(shutdown()));

    Q_ASSERT(result);

    // Set the autoExit() signal to false to control
    // the app's exit.

	Application::instance()->setAutoExit(false);

	QmlDocument *qml = QmlDocument::create("asset:///main.qml");

	if (!qml->hasErrors()) {
		OpenGLView::setRenderingAPI(GL_ES_1);

		myView = new MyView(DISPLAY_DEVICE);

		qml->setContextProperty("myView", myView);

	    // The application NavigationPane is created from QML.
	    navPane = qml->createRootObject<NavigationPane>();
	    if (navPane) {
			fwc_bound = false;

	        foreignWindow = navPane->
				findChild<ForeignWindowControl*>
					("myForeignWindow");

	        LayoutUpdateHandler *handler = 
				LayoutUpdateHandler::create(foreignWindow)
	        	  .onLayoutFrameChanged(this, 
					SLOT(onLayoutFrameChanged(const QRectF &)));


	    	// connect ForeignWindowControl signals to slots
	    	QObject::connect(foreignWindow, 
						SIGNAL(touch(bb::cascades::TouchEvent *)),
	    				myView,   
						SLOT(onTouch(bb::cascades::TouchEvent *)) );

	        // Finally the main scene for the application is set the Page.
	        Application::instance()->setScene(navPane);
		}
	}  
}
void ApplicationUI::shutdown() {

	OpenGLView::shutdown();

	Application::instance()->quit();
}
void ApplicationUI::onLayoutFrameChanged(const QRectF &layoutFrame) {
	if (fwc_bound == false) {
		fwc_bound = true;

        //obtain the window group
		QString mainWindowGroupId = 
			Application::instance()->mainWindow()->groupId();

		myView->setWindowGroup(mainWindowGroupId);
		myView->setWindowID("MyView");
		myView->setPosition(layoutFrame.x(), layoutFrame.y());
		myView->setSize(layoutFrame.width(), layoutFrame.height());
		myView->add();
		myView->setEnabled(true);
		foreignWindow->setVisible(true);
    } else {
		myView->setAngle
			(OrientationSupport::instance()->displayDirection());
		myView->setPosition(layoutFrame.x(), layoutFrame.y());
		myView->setSize(layoutFrame.width(), layoutFrame.height());
		myView->setAltered(true);
	}
}	
#include <QtCore/QObject>
#include <QtCore/QMetaType>

#include "MyView.h"
#include "OpenGLThread.hpp"

#include <bb/cascades/Application>
#include <bb/cascades/Event>
#include <bb/cascades/UiObject>
#include <bb/cascades/Control>
#include <bb/cascades/ForeignWindowControl>
#include <bb/cascades/LayoutUpdateHandler>
#include <bb/cascades/NavigationPane>
#include <bb/cascades/OrientationSupport>
#include <bb/cascades/UIOrientation>

using namespace bb::cascades;

class ApplicationUI : public QObject
{
    Q_OBJECT
public:
    ApplicationUI(bb::cascades::Application *app);
    virtual ~ApplicationUI() { };

private Q_SLOTS:
    void shutdown();
    void onLayoutFrameChanged(const QRectF &layoutFrame);
private:
    bool fwc_bound;
    NavigationPane *navPane;
    ForeignWindowControl *foreignWindow;
    MyView *myView;
};
#include <bb/cascades/Application>

#include <QLocale>
#include <QTranslator>
#include "applicationui.hpp"

#include <Qt/qdeclarativedebug.h>

using namespace bb::cascades;

Q_DECL_EXPORT int main(int argc, char **argv)
{
    Application app(argc, argv);

    // Create the Application UI object, this is where the main.qml file
    // is loaded and the application scene is set.
    new ApplicationUI(&app);

    // Enter the application main event loop.
    return Application::exec();
}
#include <assert.h>
#include <screen/screen.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <bps/navigator.h>
#include <bps/screen.h>
#include <bps/bps.h>
#include <bps/event.h>

#include <EGL/egl.h>
#include <GLES/gl.h>

#include <bb/cascades/TouchEvent>

#include <QtCore/QObject>
#include <QtCore/QString>

#include "OpenGLView.hpp"
#include "OpenGLThread.hpp"

class MyView : public OpenGLView {

	Q_OBJECT
public:
	MyView(VIEW_DISPLAY display);
	virtual ~MyView();

	int initialize();
	int regenerate();
	void cleanup();

	void handleScreenEvent(bps_event_t *event);

	void update();
	void render();
private:

    static float vertices[];
    static float colors[];

};
#include "MyView.h"
#include <math.h>

#include <QDebug>
#include <QFile>
using namespace bb::cascades;

float MyView::vertices[] = {
      // front
      -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
       0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f,

      // right
      0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
      0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f,

      // back
      0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f,
      -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,

      // left
      -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, 0.5f,
      -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f,

      // top
      -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
       0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,

      // bottom
      -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f,
       0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f
};

float MyView::colors[] = {
       /* front  */  0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f,
       /* right  */  0.29296875f,0.66796875f,0.92578125f,1.0f,0.29296875f,0.66796875f,0.92578125f,1.0f, 0.29296875f,0.66796875f,0.92578125f,1.0f, 0.29296875f,0.66796875f,0.92578125f,1.0f, 0.29296875f,0.66796875f,0.92578125f,1.0f, 0.29296875f,0.66796875f,0.92578125f,1.0f,
       /* back   */  0.52734375f,0.76171875f,0.92578125f,1.0f, 0.52734375f,0.76171875f,0.92578125f,1.0f,0.52734375f,0.76171875f,0.92578125f,1.0f, 0.52734375f,0.76171875f,0.92578125f,1.0f, 0.52734375f,0.76171875f,0.92578125f,1.0f, 0.52734375f,0.76171875f,0.92578125f,1.0f,
       /* left   */  0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f,0.0625f,0.57421875f,0.92578125f,1.0f, 0.0625f,0.57421875f,0.92578125f,1.0f,
       /* top    */  0.29296875f,0.66796875f,0.92578125f,1.0f, 0.29296875f,0.66796875f,0.92578125f,1.0f,0.29296875f,0.66796875f,0.92578125f,1.0f,0.29296875f,0.66796875f,0.92578125f,1.0f,0.29296875f,0.66796875f,0.92578125f,1.0f,0.29296875f,0.66796875f,0.92578125f,1.0f,
       /* bottom */  0.52734375f,0.76171875f,0.92578125f,1.0f,0.52734375f,0.76171875f,0.92578125f,1.0f,0.52734375f,0.76171875f,0.92578125f,1.0f,0.52734375f,0.76171875f,0.92578125f,1.0f,0.52734375f,0.76171875f,0.92578125f,1.0f,0.52734375f,0.76171875f,0.92578125f,1.0f
};
MyView::MyView(VIEW_DISPLAY display) {
	setDisplay(display);
}

MyView::~MyView() {
	// TODO Auto-generated destructor stub
}
void MyView::update() {

}
void MyView::cleanup(){
    OpenGLView::cleanup();
}
 
int MyView::regenerate()
{
    int returnCode = OpenGLView::regenerate();
    return returnCode;
}

void MyView::handleScreenEvent(bps_event_t *event) {
    int screenEvent;
    int buttons;
    int position[2];
 
    screen_event_t screen_event = screen_event_get_event(event);
 
    //Query type of screen event and its location on the screen
    screen_get_event_property_iv(screen_event, 
								SCREEN_PROPERTY_TYPE,
            					&screenEvent);

    screen_get_event_property_iv(screen_event, 
								SCREEN_PROPERTY_SOURCE_POSITION,
           					 position);
 
    switch (screenEvent) {
        case SCREEN_EVENT_MTOUCH_TOUCH:
            break;
 
        case SCREEN_EVENT_MTOUCH_MOVE:
            break;
 
        case SCREEN_EVENT_MTOUCH_RELEASE:
            break;
 
        case SCREEN_EVENT_POINTER:
            screen_get_event_property_iv(screen_event,
										SCREEN_PROPERTY_BUTTONS,
                						&buttons);
 
            switch (buttons) {
                case SCREEN_LEFT_MOUSE_BUTTON:
                case SCREEN_RIGHT_MOUSE_BUTTON:
                    //handleClick(position[0], position[1]);
                    break;
            }
 
            break;
    }
}
int MyView::initialize(){

	setZ(-5);

	int returnCode = OpenGLView::initGL();
	if (returnCode == EXIT_SUCCESS) {

		setStale(true);
        EGLint surface_width, surface_height;

        eglQuerySurface(m_egl_disp, 
						m_egl_surf, 
						EGL_WIDTH, 
						&surface_width);

        eglQuerySurface(m_egl_disp,
						m_egl_surf, 
						EGL_HEIGHT, 
						&surface_height);

        EGLint err = eglGetError();
        if (err != 0x3000) {
            fprintf(stderr, "Unable to query EGL surface dimensions\n");
            return EXIT_FAILURE;
        }

        glClearDepthf(1.0f);
        glEnable(GL_CULL_FACE);
        glShadeModel(GL_SMOOTH);

        //set clear color to black
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        glViewport(0, 0, surface_width, surface_height);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        float nearClip = -2.0f;
        float farClip  = 2.0f;
        float yFOV  = 75.0f;
        float yMax = nearClip * tan(yFOV*M_PI/360.0f);
        float aspect = surface_width/surface_height;
        float xMin = -yMax * aspect;
        float xMax = yMax *aspect;

        glFrustumf(xMin, xMax, -yMax, yMax, nearClip, farClip);

        if (surface_width > surface_height){
            glScalef(
				(float)surface_height/(float)surface_width,
				1.0, 1.0f);
        } else {
            glScalef(1.0, 
				(float)surface_width/(float)surface_height,
				1.0f);
        } 
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
	}

    return returnCode;
}
void MyView::render(){

	setStale(false);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, vertices);

    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(4, GL_FLOAT, 0, colors);

    glRotatef(1.0f, 1.0f, 1.0f, 0.0f);

    glDrawArrays(GL_TRIANGLES, 0 , 36);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);

	setStale(true);

}
/*
 * Copyright (c) 2011-2012 Research In Motion Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#ifndef OPENGLVIEW_HPP
#define OPENGLVIEW_HPP

#include <stdlib.h>
#include <screen/screen.h>
#include <sys/platform.h>

#include <bps/navigator.h>
#include <bps/screen.h>
#include <bps/bps.h>
#include <bps/event.h>

#include <EGL/egl.h>
#include <GLES/gl.h>
#include <GLES/glext.h>

#include <ft2build.h>
#include FT_FREETYPE_H

#include "png.h"

#include <QtCore/QObject>
#include <QtCore/QMutex>
#include <QtCore/QVariant>

#include <pthread.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct font_t {
	unsigned int font_texture;
	float pt;
	float advance[128];
	float width[128];
	float height[128];
	float tex_x1[128];
	float tex_x2[128];
	float tex_y1[128];
	float tex_y2[128];
	float offset_x[128];
	float offset_y[128];
	int initialized;
} font_t;

#ifdef __cplusplus
}
#endif

typedef enum RENDERING_API {GL_UNKNOWN = 0,
							GL_ES_1 = EGL_OPENGL_ES_BIT, 
							GL_ES_2 = EGL_OPENGL_ES2_BIT, 
							VG = EGL_OPENVG_BIT} RENDERING_API;

typedef enum VIEW_DISPLAY {DISPLAY_UNKNOWN, 
						   DISPLAY_DEVICE,
						   DISPLAY_HDMI} VIEW_DISPLAY;


class OpenGLView : public QObject {

	Q_OBJECT

	Q_PROPERTY(bool enabled READ enabled WRITE setEnabled)

	friend class OpenGLThread;

public:
	OpenGLView();

	virtual ~OpenGLView();

	// must be defined in derived classes
	virtual int initialize() = 0;

	// display functions
	void setDisplay(VIEW_DISPLAY display);

	// handle screen events when view 
	// is an overlay ie. above Cascades layer
	virtual void handleScreenEvent(bps_event_t *event) = 0;

	// state functions
	// enable is used to enable / disable rendering
	bool enabled();
	// flags initialization of the view
	bool initialized();

	// stale flag indicates when the view 
	// needs to be rendered again
	bool stale();

	// this flag signals that the view was altered
	// (rotated or resized) and needs to be regenerated
	bool altered();

	// custom update handling to be defined in derived class
	virtual void update() = 0;

	// main render function - obtains GL context 
	// and swaps buffer so that the derived class 
	// doesn't need to handle it
	void renderView();

	// custom rendering to be defined in base class
	virtual void render() = 0;

	// view settings
	void setAngle(int angle);
	void setPosition(int x, int y);
	void setSize(int width, int height);
	void setZ(int z);
	void setTransparency(int transparency);

	void setWindowGroup(const QString &group);
	void setWindowID(const QString id);

	// called by the thread to regenerate the surface
	virtual int regenerate();

	// custom cleanup to be implemented by derived class
	virtual void cleanup() = 0;

	// functions to add or remove 
	// this view - hide thread functions from caller
	void add();
	void remove();

	// function to set GL API
	static void setRenderingAPI(RENDERING_API api);

	// shutdown - call thread function, hiding it from caller
	static void shutdown();


public Q_SLOTS:
	// state slots
	void setEnabled(bool enabled);
	void setInitialized(bool initialized);
	void setStale(bool stale);
	void setAltered(bool stale);

protected:
	// init GL - must be called in derived initialize function
	int initGL();

	bool visible();
	void setVisible(bool visible);


	// display functions
	EGLDisplay display();

	// API / screen property calls
	void setAPI(RENDERING_API api);
	void setScreenContext(screen_context_t screen_ctx);
	void setScreenEGLConfiguration(EGLConfig egl_conf);
	void setScreenEGLDisplay(EGLDisplay egl_disp);

	// window property calls
	int setWindowAngle(int angle);
	int setWindowPosition(int x, int y);
	int setWindowSize(int width, int height);
	int setWindowZ(int z);
	int setWindowSourceSize(int width, int height);
	int setWindowBufferSize(int width, int height);
	int setWindowTransparency(int transparency);
	int setWindowUsage(int usage);

	// window group / ID calls
	int joinWindowGroup(const QString &group);
	int setScreenWindowID(const QString id);


	// OpenGL utility functions for OpenGL views - derived from bb_util.c

	/**
	 * Swaps default window surface to the screen
	 */
	void swapBuffers();

	/*
	 * makes this view's EGL context curent
	 */
	void getGLContext();

	/*
	 * releases the view's EGL context from being current
	 */
	void  releaseGLContext();

	/**
	 * Loads the font from the specified font file.
	 * NOTE: should be called after a successful return from bbutil_init() or bbutil_init_egl() call
	 * @param font_file string indicating the absolute path of the font file
	 * @param point_size used for glyph generation
	 * @param dpi used for glyph generation
	 * @return pointer to font_t structure on success or NULL on failure
	 */
	font_t* loadFont(const char* font_file, int point_size, int dpi);

	/**
	 * Destroys the passed font
	 * @param font to be destroyed
	 */
	void destroyFont(font_t* font);

	/**
	 * Renders the specified message using current font starting from the specified
	 * bottom left coordinates.
	 * NOTE: must be called after a successful return from bbutil_init() or bbutil_init_egl() call

	 *
	 * @param font to use for rendering
	 * @param msg the message to display
	 * @param x, y position of the bottom-left corner of text string in world coordinate space
	 * @param rgba color for the text to render with
	 */
	void renderText(font_t* font, const char* msg, float x, float y, float r, float g, float b, float a);

	/**
	 * Returns the non-scaled width and height of a string
	 * NOTE: must be called after a successful return from bbutil_init() or bbutil_init_egl() call

	 *
	 * @param font to use for measurement of a string size
	 * @param msg the message to get the size of
	 * @param return pointer for width of a string
	 * @param return pointer for height of a string
	 */
	void measureText(font_t* font, const  char* msg, float* width, float* height);

	/**
	 * Creates and loads a texture from a png file
	 * NOTE: must be called after a successful return from bbutil_init() or bbutil_init_egl() call

	 *
	 * @param filename path to texture png
	 * @param return width of texture
	 * @param return height of texture
	 * @param return gl texture handle
	 * @return EXIT_SUCCESS if texture loading succeeded otherwise EXIT_FAILURE
	 */

	int loadTexture(const char* filename, int* width, int* height, float* tex_x, float* tex_y, unsigned int* tex);

	/**
	 * Returns dpi for current screen
	 *
	 * @return dpi for current screen
	 */

	int calculateDPI();

protected:
	// state
	bool m_enabled;
	bool m_initialized;
	bool m_altered;
	bool m_visible;
	bool m_stale;
	VIEW_DISPLAY m_display;

	// EGL parameters
	EGLContext m_egl_ctx;
	EGLConfig m_egl_conf;
	EGLDisplay m_egl_disp;
	EGLSurface m_egl_surf;
	int m_usage;
	int m_nbuffers;

	EGLint m_surface_width;
	EGLint m_surface_height;

    RENDERING_API m_api;

	// screens / windows
	screen_context_t m_screen_ctx;
	screen_window_t m_screen_win;
	screen_display_t m_screen_disp;
	screen_display_t *m_screen_dpy;
	screen_display_mode_t *m_screen_modes;

	// view properties
	int m_angle;
	int m_x;
	int m_y;
	int m_z;
	int m_width;
	int m_height;
	int m_interval;
	int m_transparency;

	// window group / ID
	QString m_group;
	QString m_id;

	// main mutex for controlling view access
	QMutex m_viewMutex;

private:
	int nextp2(int x);

	// mutex for controlling render access across all views
	static QMutex m_renderMutex;
};

#endif /* OPENGLVIEW_HPP */

#include "OpenGLView.hpp"
#include "OpenGLThread.hpp"

#include <QDebug>

QMutex OpenGLView::m_renderMutex;

void OpenGLView::setRenderingAPI(RENDERING_API api)
{
	OpenGLThread::getInstance()->setRenderingAPI(api);
}

OpenGLView::OpenGLView() : QObject(OpenGLThread::getInstance())
{
	// initialize members shared by derived classes
	m_nbuffers = 2;
	m_enabled = false;
	m_initialized = false;
	m_stale = false;
	m_altered = false;
	m_visible = false;

	m_screen_win = NULL;
	m_screen_dpy = NULL;

	m_angle = 0.0;
	m_x = 0;
	m_y = 0;
	m_width = 0;
	m_height = 0;
	m_interval = 0;
	m_transparency = 0;
}

OpenGLView::~OpenGLView() {
}

int OpenGLView::initGL()
{
    int numberDisplays;
    int numberModes;
    int returnCode;
    EGLBoolean status;
    int type;
    EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
	
    EGLint attrib_list[]= { EGL_RED_SIZE,        8,
                            EGL_GREEN_SIZE,      8,
                            EGL_BLUE_SIZE,       8,
                            EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
                            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
                            EGL_NONE};

    // try this first as it will fail if an HDMI display is not attached
    if (m_api == GL_ES_2) {
        m_egl_ctx = eglCreateContext(m_egl_disp, m_egl_conf, EGL_NO_CONTEXT, attributes);
    } else {
        m_egl_ctx = eglCreateContext(m_egl_disp, m_egl_conf, EGL_NO_CONTEXT, NULL);
    }
    if (m_egl_ctx == EGL_NO_CONTEXT) {
        perror("eglCreateContext");
        return EXIT_FAILURE;
    }

	screen_get_context_property_iv(m_screen_ctx, SCREEN_PROPERTY_DISPLAY_COUNT, &numberDisplays);

	m_screen_dpy = (screen_display_t *)calloc(numberDisplays, sizeof(screen_display_t));
	screen_get_context_property_pv(m_screen_ctx, SCREEN_PROPERTY_DISPLAYS, (void **)m_screen_dpy);


	for (int index = 0; index < numberDisplays; index++) {
		int displayID;

        returnCode = screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_ID,  (int *)&displayID);
    	if (returnCode) {
    		perror("display ID");
    		return EXIT_FAILURE;
    	} else {
			if (displayID == m_display) {
			    screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_TYPE,  &type);
			    if (type == SCREEN_DISPLAY_TYPE_HDMI) {
			    	returnCode = screen_create_window(&m_screen_win, m_screen_ctx);
			    	if (returnCode) {
			            perror("screen_create_window");
			            return EXIT_FAILURE;
			        }
			    } else {
			    	returnCode = screen_create_window_type(&m_screen_win, m_screen_ctx, SCREEN_CHILD_WINDOW);
			    	if (returnCode) {
			            perror("screen_create_window (child window)");
			            return EXIT_FAILURE;
			        }
			    }
			    if (type == SCREEN_DISPLAY_TYPE_HDMI) {
					returnCode = screen_set_window_property_pv(m_screen_win, SCREEN_PROPERTY_DISPLAY, (void **)&(m_screen_dpy[index]));
					if (returnCode) {
						perror("window display");
						return EXIT_FAILURE;
					}
		        }
			}
        }
	}

	qDebug() << "OpenGLView::initialize:" << m_screen_ctx << ":" << m_egl_disp << ":" << m_egl_conf << ":" << m_egl_ctx << ":" << m_screen_win;


	int format = SCREEN_FORMAT_RGBA8888;
	returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_FORMAT, &format);
	if (returnCode) {
		perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)");
		return EXIT_FAILURE;
	}

	if (m_transparency > 0) {
		returnCode = setWindowTransparency(m_transparency);
		if (returnCode) {
			perror("transparency");
			return EXIT_FAILURE;
		}
	}

	returnCode = screen_get_window_property_pv(m_screen_win, SCREEN_PROPERTY_DISPLAY, (void **)&m_screen_disp);
	if (returnCode) {
		perror("screen_get_window_property_pv");
		return EXIT_FAILURE;
	}

	int angle = atoi(getenv("ORIENTATION"));

	screen_get_display_property_iv(m_screen_disp, SCREEN_PROPERTY_MODE_COUNT, &numberModes);

	m_screen_modes = (screen_display_mode_t *)calloc(numberModes, sizeof(screen_display_mode_t));
	returnCode = screen_get_display_property_pv(m_screen_disp, SCREEN_PROPERTY_MODE, (void**)m_screen_modes);
	if (returnCode) {
		perror("screen modes");
		return EXIT_FAILURE;
	}

    int dpi = calculateDPI();
    if (dpi == EXIT_FAILURE) {
        fprintf(stderr, "Unable to calculate dpi\n");
        return EXIT_FAILURE;
    }

	returnCode = setWindowPosition(m_x, m_y);
	if (returnCode) {
		perror("window position");
		return EXIT_FAILURE;
	}

	returnCode = setWindowSize(m_width, m_height);
	if (returnCode) {
		perror("window size");
		return EXIT_FAILURE;
	}

	returnCode = setWindowZ(m_z);
	if (returnCode) {
		perror("z order");
		return EXIT_FAILURE;
	}

	returnCode = setWindowBufferSize(m_width, m_height);
	if (returnCode) {
		perror("buffer size");
		return EXIT_FAILURE;
	}

	returnCode = setWindowAngle(m_angle);
	if (returnCode) {
		perror("angle");
		return EXIT_FAILURE;
	}

	returnCode = screen_create_window_buffers(m_screen_win, m_nbuffers);
	if (returnCode) {
		perror("screen_create_window_buffers");
		return EXIT_FAILURE;
	}


    if (m_api == GL_ES_1) {
        m_usage = SCREEN_USAGE_OPENGL_ES1 | SCREEN_USAGE_ROTATION;
    } else if (m_api == GL_ES_2) {
    	attrib_list[9] = EGL_OPENGL_ES2_BIT;
    	m_usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_ROTATION;
    } else if (m_api == VG) {
    	attrib_list[9] = EGL_OPENVG_BIT;
    	m_usage = SCREEN_USAGE_OPENVG | SCREEN_USAGE_ROTATION;
    } else {
        fprintf(stderr, "invalid api setting\n");
        return EXIT_FAILURE;
    }

	returnCode = setWindowUsage(m_usage);
	if (returnCode) {
		perror("screen_set_window_property_iv(window usage)");
		return EXIT_FAILURE;
	}

	qDebug() << "OpenGLView::initGL:eglCreateContext" << m_egl_ctx;
	m_egl_surf = eglCreateWindowSurface(m_egl_disp, m_egl_conf, m_screen_win, NULL);
	if (m_egl_surf == EGL_NO_SURFACE) {
		OpenGLThread::eglPrintError("eglCreateWindowSurface");
		return EXIT_FAILURE;
	}

	getGLContext();

    EGLint interval = 1;
    status = eglSwapInterval(m_egl_disp, interval);
	if (status != EGL_TRUE) {
		OpenGLThread::eglPrintError("eglSwapInterval");
		return EXIT_FAILURE;
	}

    status = eglQuerySurface(m_egl_disp, m_egl_surf, EGL_WIDTH, &m_surface_width);
	if (status != EGL_TRUE) {
		perror("query surface width");
		return EXIT_FAILURE;
	}

    status = eglQuerySurface(m_egl_disp, m_egl_surf, EGL_HEIGHT, &m_surface_height);
	if (status != EGL_TRUE) {
		perror("query surface height");
		return EXIT_FAILURE;
	}

	returnCode = joinWindowGroup(m_group);
	if (returnCode) {
		perror("window group");
		return EXIT_FAILURE;
	}

	returnCode = setScreenWindowID(m_id);
	if (returnCode) {
		perror("window ID");
		return EXIT_FAILURE;
	}

	qDebug() << "OpenGLView::initGL:" << angle << ":" << numberModes << ":" << m_screen_modes[0].width << ":" << m_screen_modes[0].height << ":" << m_egl_disp << ":" << dpi;

	setInitialized(true);

	return EXIT_SUCCESS;
}

EGLDisplay OpenGLView::display()
{
	return m_egl_disp;
}

void OpenGLView::setDisplay(VIEW_DISPLAY display)
{
	m_display = display;
	m_egl_disp = OpenGLThread::getInstance()->getDisplay(display);
	setScreenEGLDisplay(m_egl_disp);
}

void OpenGLView::setScreenContext(screen_context_t screen_ctx)
{
    m_screen_ctx = screen_ctx;
}


void OpenGLView::setAPI(RENDERING_API api)
{
	m_api = api;
}

void OpenGLView::setScreenEGLConfiguration(EGLConfig egl_conf)
{
    m_egl_conf = egl_conf;
}

void OpenGLView::setScreenEGLDisplay(EGLDisplay egl_disp)
{
    m_egl_disp = egl_disp;
}

void OpenGLView::shutdown() {
	OpenGLThread::getInstance()->shutdown();

	while (OpenGLThread::getInstance()->isRunning()) {
		usleep(100);
	}

}

void OpenGLView::cleanup() {
    if (m_egl_disp != EGL_NO_DISPLAY) {
    	releaseGLContext();
		if (m_egl_ctx != EGL_NO_CONTEXT) {
			eglDestroyContext(m_egl_disp, m_egl_ctx);
			m_egl_ctx = EGL_NO_CONTEXT;
		}
		if (m_egl_surf != EGL_NO_SURFACE) {
			eglDestroySurface(m_egl_disp, m_egl_surf);
			m_egl_surf = EGL_NO_SURFACE;
		}
    }

	if (m_screen_win != NULL) {
		screen_destroy_window(m_screen_win);
		m_screen_win = NULL;
	}

	if (m_screen_dpy != NULL) {
		free(m_screen_dpy);
	}

	if (m_screen_win != NULL) {
		free(m_screen_modes);
	}
}

void OpenGLView::setEnabled(bool enabled) {
	m_enabled = enabled;
}

bool OpenGLView::enabled() {
	return m_enabled;
}

void OpenGLView::setInitialized(bool initialized)
{
	m_viewMutex.lock();

	m_initialized = initialized;

	m_viewMutex.unlock();
}

bool OpenGLView::initialized() {
	bool initialized;

	m_viewMutex.lock();

	initialized = m_initialized;

	m_viewMutex.unlock();

	return initialized;
}

void OpenGLView::setStale(bool stale)
{
	m_viewMutex.lock();

	m_stale = stale;

	m_viewMutex.unlock();
}

bool OpenGLView::stale() {
	bool stale;

	m_viewMutex.lock();

	stale = m_stale;

	m_viewMutex.unlock();

	return stale;
}

void OpenGLView::setAltered(bool altered)
{
	m_viewMutex.lock();

	m_altered = altered;

	m_viewMutex.unlock();
}

bool OpenGLView::altered() {
	bool altered;

	m_viewMutex.lock();

	altered = m_altered;

	m_viewMutex.unlock();

	return altered;
}

void OpenGLView::setVisible(bool visible)
{
	m_viewMutex.lock();

	m_visible = visible;

	m_viewMutex.unlock();
}

bool OpenGLView::visible() {
	bool visible;

	m_viewMutex.lock();

	visible = m_visible;

	m_viewMutex.unlock();

	return visible;
}

void OpenGLView::setAngle(int angle)
{
	if ((angle != 0) && (angle != 90) && (angle != 180) && (angle != 270)) {
		fprintf(stderr, "Invalid angle\n");
	} else {
		m_angle = angle;
	}
}

void OpenGLView::setPosition(int x, int y)
{
	m_x = x;
	m_y = y;
}

void OpenGLView::setSize(int width, int height)
{
	m_width = width;
	m_height = height;
}

void OpenGLView::setZ(int z)
{
	m_z = z;
}

void OpenGLView::setTransparency(int transparency)
{
	m_transparency = transparency;
}

void OpenGLView::setWindowGroup(const QString &group)
{
	m_group = group;
}

void OpenGLView::setWindowID(const QString id)
{
	m_id = id;
}

int OpenGLView::setWindowAngle(int angle)
{
	int returnCode;

	if (m_screen_win != NULL) {
		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_ROTATION, &angle);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setWindowPosition(int x, int y)
{
	int returnCode;
	int position[2];

	if (m_screen_win != NULL) {
		position[0] = x;
		position[1] = y;

		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_POSITION, position);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setWindowSize(int width, int height)
{
	int returnCode;
	int size[2];

	if (m_screen_win != NULL) {
		size[0] = width;
		size[1] = height;

		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_SIZE, size);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setWindowZ(int z)
{
	int returnCode = 0;

	if (m_screen_win != NULL) {
		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_ZORDER, &z);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setWindowTransparency(int transparency)
{
	int returnCode = 0;

	if (m_screen_win != NULL) {
		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_TRANSPARENCY, &transparency);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setWindowUsage(int usage)
{
	int returnCode = 0;

	if (m_screen_win != NULL) {
		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_USAGE, &usage);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setWindowSourceSize(int width, int height)
{
	int returnCode;
	int size[2];

	if (m_screen_win != NULL) {
		size[0] = width;
		size[1] = height;

		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_SOURCE_SIZE, size);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setWindowBufferSize(int width, int height)
{
	int returnCode;
	int size[2];

	if (m_screen_win != NULL) {
		size[0] = width;
		size[1] = height;

		returnCode = screen_set_window_property_iv(m_screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size);
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::joinWindowGroup(const QString &group)
{
	int returnCode = 0;

	if (m_screen_win != NULL) {
		returnCode = screen_join_window_group(m_screen_win, group.toAscii());
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}

int OpenGLView::setScreenWindowID(const QString id)
{
	int returnCode = 0;

	if (m_screen_win != NULL) {
		returnCode = screen_set_window_property_cv(m_screen_win, SCREEN_PROPERTY_ID_STRING, id.toAscii().length(), id.toAscii());
	} else {
		returnCode = EXIT_SUCCESS;
	}

	return returnCode;
}



int OpenGLView::regenerate() {
	int returnCode;
	EGLBoolean status;
	EGLint interval = 1;

	OpenGLView::m_renderMutex.lock();

    status = eglQuerySurface(m_egl_disp, m_egl_surf, EGL_WIDTH, &m_surface_width);
	if (status != EGL_TRUE) {
		perror("query surface width");
		return EXIT_FAILURE;
	}

    status = eglQuerySurface(m_egl_disp, m_egl_surf, EGL_HEIGHT, &m_surface_height);
	if (status != EGL_TRUE) {
		perror("query surface height");
		return EXIT_FAILURE;
	}

/*
	rc = screen_get_window_property_iv(m_screen_win, SCREEN_PROPERTY_ROTATION, &rotation);
	if (rc) {
		perror("screen_set_window_property_iv");
		return EXIT_FAILURE;
	}

	rc = screen_get_window_property_iv(m_screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size);
	if (rc) {
		perror("screen_set_window_property_iv");
		return EXIT_FAILURE;
	}

	switch (angle - rotation) {
		case -270:
		case -90:
		case 90:
		case 270:
			temp = size[0];
			size[0] = size[1];
			size[1] = temp;
			skip = 0;
			break;
	}
*/

	status = eglMakeCurrent(m_egl_disp, NULL, NULL, NULL);
	if (status != EGL_TRUE) {
		OpenGLThread::eglPrintError("eglMakeCurrent");
		return EXIT_FAILURE;
	}

	status = eglDestroySurface(m_egl_disp, m_egl_surf);
	if (status != EGL_TRUE) {
		OpenGLThread::eglPrintError("eglMakeCurrent");
		return EXIT_FAILURE;
	}

	returnCode = setWindowPosition(m_x, m_y);
	if (returnCode) {
		perror("window position");
		return EXIT_FAILURE;
	}

	returnCode = setWindowSize(m_width, m_height);
	if (returnCode) {
		perror("window size");
		return EXIT_FAILURE;
	}
/*
	setWindowAngle(m_angle);
	if (returnCode) {
		perror("window angle");
		return EXIT_FAILURE;
	}
*/
	returnCode = setWindowSourceSize(m_width, m_height);
	if (returnCode) {
		perror("unable to set window source size");
		return EXIT_FAILURE;
	}

	returnCode = setWindowBufferSize(m_width, m_height);
	if (returnCode) {
		perror("buffer size");
		return EXIT_FAILURE;
	}

	m_egl_surf = eglCreateWindowSurface(m_egl_disp, m_egl_conf, m_screen_win, NULL);
	if (m_egl_surf == EGL_NO_SURFACE) {
		OpenGLThread::eglPrintError("eglCreateWindowSurface");
		return EXIT_FAILURE;
	}

	getGLContext();

    status = eglSwapInterval(m_egl_disp, interval);
	if (status != EGL_TRUE) {
		OpenGLThread::eglPrintError("eglSwapInterval");
		return EXIT_FAILURE;
	}

	OpenGLView::m_renderMutex.unlock();

	setAltered(false);

	setStale(true);

	return EXIT_SUCCESS;
}

void OpenGLView::getGLContext()
{
	EGLBoolean status;

	if (m_egl_ctx != eglGetCurrentContext()) {
		status = eglMakeCurrent(m_egl_disp, m_egl_surf, m_egl_surf, m_egl_ctx);
		if (status != EGL_TRUE) {
			OpenGLThread::eglPrintError("getGLContext (eglMakeCurrent)");
		}
	}
}

void OpenGLView::releaseGLContext()
{
	EGLBoolean status;

	if (m_egl_ctx != NULL) {
		status = eglMakeCurrent(m_egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
		if (status != EGL_TRUE) {
			OpenGLThread::eglPrintError("releaseGLContext (eglMakeCurrent)");
		}
	}
}

void OpenGLView::swapBuffers() {
	EGLBoolean status;

    status = eglSwapBuffers(m_egl_disp, m_egl_surf);
    if (status != EGL_TRUE) {
        OpenGLThread::eglPrintError("eglSwapBuffers");
    }

}

void OpenGLView::renderView()
{
	OpenGLView::m_renderMutex.lock();

	getGLContext();

	render();

	swapBuffers();

	OpenGLView::m_renderMutex.unlock();
}

void OpenGLView::add()
{
	OpenGLThread::getInstance()->addView(this);
}

void OpenGLView::remove()
{
	OpenGLThread::getInstance()->removeView(this);
}


/* Finds the next power of 2 */
int OpenGLView::nextp2(int x)
{
    int val = 1;
    while(val < x) val <<= 1;
    return val;
}

int OpenGLView::loadTexture(const char* filename, int* width, int* height, float* tex_x, float* tex_y, unsigned int *tex) {
    int i;
    GLuint format;
    //header for testing if it is a png
    png_byte header[8];

    if (!tex) {
        return EXIT_FAILURE;
    }

    //open file as binary
    FILE *fp = fopen(filename, "rb");
    if (!fp) {
        return EXIT_FAILURE;
    }

    //read the header
    fread(header, 1, 8, fp);

    //test if png
    int is_png = !png_sig_cmp(header, 0, 8);
    if (!is_png) {
        fclose(fp);
        return EXIT_FAILURE;
    }

    //create png struct
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
        fclose(fp);
        return EXIT_FAILURE;
    }

    //create png info struct
    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
        fclose(fp);
        return EXIT_FAILURE;
    }

    //create png info struct
    png_infop end_info = png_create_info_struct(png_ptr);
    if (!end_info) {
        png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
        fclose(fp);
        return EXIT_FAILURE;
    }

    //setup error handling (required without using custom error handlers above)
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        fclose(fp);
        return EXIT_FAILURE;
    }

    //init png reading
    png_init_io(png_ptr, fp);

    //let libpng know you already read the first 8 bytes
    png_set_sig_bytes(png_ptr, 8);

    // read all the info up to the image data
    png_read_info(png_ptr, info_ptr);

    //variables to pass to get info
    int bit_depth, color_type;
    png_uint_32 image_width, image_height;

    // get info about png
    png_get_IHDR(png_ptr, info_ptr, &image_width, &image_height, &bit_depth, &color_type, NULL, NULL, NULL);

    switch (color_type)
    {
        case PNG_COLOR_TYPE_RGBA:
            format = GL_RGBA;
            break;
        case PNG_COLOR_TYPE_RGB:
            format = GL_RGB;
            break;
        default:
            fprintf(stderr,"Unsupported PNG color type (%d) for texture: %s", (int)color_type, filename);
            fclose(fp);
            png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
            return EXIT_FAILURE;
    }

    // Update the png info struct.
    png_read_update_info(png_ptr, info_ptr);

    // Row size in bytes.
    int rowbytes = png_get_rowbytes(png_ptr, info_ptr);

    // Allocate the image_data as a big block, to be given to opengl
    png_byte *image_data = (png_byte*) malloc(sizeof(png_byte) * rowbytes * image_height);

    if (!image_data) {
        //clean up memory and close stuff
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        fclose(fp);
        return EXIT_FAILURE;
    }

    //row_pointers is for pointing to image_data for reading the png with libpng
    png_bytep *row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * image_height);
    if (!row_pointers) {
        //clean up memory and close stuff
        png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
        free(image_data);
        fclose(fp);
        return EXIT_FAILURE;
    }

    // set the individual row_pointers to point at the correct offsets of image_data
    for (i = 0; i < image_height; i++) {
        row_pointers[image_height - 1 - i] = image_data + i * rowbytes;
    }

    //read the png into image_data through row_pointers
    png_read_image(png_ptr, row_pointers);

    int tex_width, tex_height;

    tex_width = nextp2(image_width);
    tex_height = nextp2(image_height);

    glGenTextures(1, tex);
    glBindTexture(GL_TEXTURE_2D, (*tex));
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glTexImage2D(GL_TEXTURE_2D, 0, format, tex_width, tex_height, 0, format, GL_UNSIGNED_BYTE, NULL);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, format, GL_UNSIGNED_BYTE, image_data);

    GLint err = glGetError();

    //clean up memory and close stuff
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    free(image_data);
    free(row_pointers);
    fclose(fp);

    if (err == 0) {
        //Return physical with and height of texture if pointers are not null
        if(width) {
            *width = image_width;
        }
        if (height) {
            *height = image_height;
        }
        //Return modified texture coordinates if pointers are not null
        if(tex_x) {
            *tex_x = ((float) image_width - 0.5f) / ((float)tex_width);
        }
        if(tex_y) {
            *tex_y = ((float) image_height - 0.5f) / ((float)tex_height);
        }
        return EXIT_SUCCESS;
    } else {
        fprintf(stderr, "GL error %i \n", err);
        return EXIT_FAILURE;
    }
}

int OpenGLView::calculateDPI() {
    int returnCode;
    int screen_phys_size[2];

	qDebug() << "OpenGLView::calculateDPI: physical:" << m_egl_disp;

    returnCode = screen_get_display_property_iv(m_screen_disp, SCREEN_PROPERTY_PHYSICAL_SIZE, screen_phys_size);
    if (returnCode) {
        perror("screen_get_display_property_iv");
        return EXIT_FAILURE;
    }

    //Simulator will return 0,0 for physical size of the screen, so use 170 as default dpi
    if ((screen_phys_size[0] == 0) && (screen_phys_size[1] == 0)) {
        return 170;
    } else {
        int screen_resolution[2];

        qDebug() << "OpenGLView::calculateDPI: screen:" << m_egl_disp;

        returnCode = screen_get_display_property_iv(m_screen_disp, SCREEN_PROPERTY_SIZE, screen_resolution);
        if (returnCode) {
            perror("screen_get_display_property_iv");
            return EXIT_FAILURE;
        }

        int diagonal_pixels = sqrt(screen_resolution[0] * screen_resolution[0] + screen_resolution[1] * screen_resolution[1]);
        int diagontal_inches = 0.0393700787 * sqrt(screen_phys_size[0] * screen_phys_size[0] + screen_phys_size[1] * screen_phys_size[1]);
        return (int)(diagonal_pixels / diagontal_inches);
    }
}

/*
 * Copyright (c) 2011-2012 Research In Motion Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef OPENGLTHREAD_HPP
#define OPENGLTHREAD_HPP

#include <assert.h>
#include <screen/screen.h>
#include <bps/navigator.h>
#include <bps/screen.h>
#include <bps/bps.h>
#include <bps/event.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <EGL/egl.h>
#include <GLES/gl.h>

#include <bb/cascades/TouchEvent>

#include <QtCore/QThread>
#include <QtCore/QList>
#include <QtCore/QString>

#include "OpenGLView.hpp"

class OpenGLThread :public QThread {

Q_OBJECT

public:
	OpenGLThread();

	virtual ~OpenGLThread();

	void run();

	static OpenGLThread *getInstance();

	// signal the thread to shut down
	void shutdown();

	// add / remove views
	void addView(OpenGLView *view);
	void removeView(OpenGLView *view);

	// get the desired display
	EGLDisplay getDisplay(VIEW_DISPLAY display);
	bool isDisplayAttached(VIEW_DISPLAY display);

	// read / set the GL rendering API
	bool renderingAPI();
	void setRenderingAPI(RENDERING_API api);

	// control the initialized state
	bool initialized();
	void setInitialized(bool initialized);

	// control the EGL initialized state
	bool eglInitialized();
	void setEGLInitialized(bool initialized);

	// control the stopped state
	bool stopped();
	void setStopped(bool initialized);

	// handy print error function derived from bb_util.c
	static void eglPrintError(const char *msg);

private:
	// initialize / cleanup functions
	int initBPS();
	int initEGL();
	void cleanupEGL();
	void cleanup();


	void update();
	void render();


	// handle screen events - only need to handle display events in the thread
	virtual void handleScreenEvent(bps_event_t *event);

	// EGL members
	EGLDisplay m_egl_disp;
	EGLDisplay m_egl_disp_hdmi;
	EGLConfig m_egl_conf;
	EGLContext m_egl_ctx;
    int m_usage;

    RENDERING_API m_api;

    // display info
    int m_numberDisplays;
    screen_display_t *m_screen_dpy;


	// screens / windows
	screen_context_t m_screen_ctx;
	float screenWidth, screenHeight;

	QVector<OpenGLView*> m_views;

	// state fields
	bool m_initialized;
	bool m_egl_initialized;
	bool m_stopped;

	// mutexes to control thread access
	QMutex m_viewsMutex;
	QMutex m_threadMutex;

	static OpenGLThread singleton;
};

#endif /* OPENGLTHREAD_HPP */

/*
 * Copyright (c) 2011-2012 Research In Motion Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "OpenGLThread.hpp"
#include <math.h>

#include <QDebug>

using namespace bb::cascades;

OpenGLThread OpenGLThread::singleton;

OpenGLThread::OpenGLThread()
{
    m_initialized = false;
    m_egl_initialized = false;
    m_stopped = false;
    m_api = GL_UNKNOWN;
    m_numberDisplays = 0;
    m_screen_dpy = NULL;
}

OpenGLThread::~OpenGLThread() {
}

bool OpenGLThread::renderingAPI()
{
	RENDERING_API api = GL_UNKNOWN;

	m_threadMutex.lock();

	api = m_api;

	m_threadMutex.unlock();

	return api;
}

void OpenGLThread::setRenderingAPI(RENDERING_API api)
{
	m_threadMutex.lock();

	m_api = api;

	m_threadMutex.unlock();
}

bool OpenGLThread::initialized()
{
	bool initialized = false;

	m_threadMutex.lock();

	initialized = m_initialized;

	m_threadMutex.unlock();

	return initialized;
}

void OpenGLThread::setInitialized(bool initialized)
{
	m_threadMutex.lock();

	m_initialized = initialized;

	m_threadMutex.unlock();
}

bool OpenGLThread::eglInitialized()
{
	bool initialized = false;

	m_threadMutex.lock();

	initialized = m_egl_initialized;

	m_threadMutex.unlock();

	return initialized;
}

void OpenGLThread::setEGLInitialized(bool initialized)
{
	m_threadMutex.lock();

	m_egl_initialized = initialized;

	m_threadMutex.unlock();
}

bool OpenGLThread::stopped()
{
	bool stopped = false;

	m_threadMutex.lock();

	stopped = m_stopped;

	m_threadMutex.unlock();

	return stopped;
}

void OpenGLThread::setStopped(bool stopped)
{
	m_threadMutex.lock();

	m_stopped = stopped;

	m_threadMutex.unlock();
}


void OpenGLThread::addView(OpenGLView *view) {
	if (view != NULL) {
		m_viewsMutex.lock();

		m_views.append(view);

		m_viewsMutex.unlock();

		while (!view->initialized() && view->visible()) {
			usleep(1);
		}
	}
}

void OpenGLThread::removeView(OpenGLView *view) {
	if (view != NULL) {
		m_viewsMutex.lock();
		int index = m_views.indexOf(view);

		if ( index != -1 ) {
			m_views.remove(index);
		}

		m_viewsMutex.unlock();

		view->cleanup();
	}
}

int OpenGLThread::initBPS() {
	//Initialize BPS library
	bps_initialize();

	//Create a screen context that will be used to create an EGL surface to to receive libscreen events
	if (EXIT_SUCCESS != screen_create_context(&m_screen_ctx, 0)) {
		fprintf(stderr, "screen_request_events failed\n");
		return EXIT_FAILURE;
	}

	// wait for rendering API to be set
	while (renderingAPI() == GL_UNKNOWN) {
		usleep(10);
	};

	// initialize EGL
	if (EXIT_SUCCESS != initEGL()) {
		fprintf(stderr, "initialize EGL failed\n");
		return EXIT_FAILURE;
	}

	//Signal BPS library that screen events will be requested
	if (BPS_SUCCESS != screen_request_events(m_screen_ctx)) {
		fprintf(stderr, "screen_request_events failed\n");
		return EXIT_FAILURE;
	}

	setInitialized(true);

	return EXIT_SUCCESS;
}

int OpenGLThread::initEGL() {
    int returnCode, type;
    int num_configs;

    m_numberDisplays = 0;
    EGLint attrib_list[]= { EGL_RED_SIZE,        8,
                            EGL_GREEN_SIZE,      8,
                            EGL_BLUE_SIZE,       8,
                            EGL_DEPTH_SIZE, 	 16,
                            EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
                            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
                            EGL_NONE};

	screen_get_context_property_iv(m_screen_ctx, SCREEN_PROPERTY_DISPLAY_COUNT, &m_numberDisplays);

	m_screen_dpy = (screen_display_t *)calloc(m_numberDisplays, sizeof(screen_display_t));
	screen_get_context_property_pv(m_screen_ctx, SCREEN_PROPERTY_DISPLAYS, (void **)m_screen_dpy);

    m_egl_disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (m_egl_disp == EGL_NO_DISPLAY) {
        eglPrintError("eglGetDisplay");
        return EXIT_FAILURE;
    }

    returnCode = eglInitialize(m_egl_disp, NULL, NULL);
    if (returnCode != EGL_TRUE) {
        eglPrintError("eglInitialize");
        return EXIT_FAILURE;
    }

    if ((m_api == GL_ES_1) || (m_api == GL_ES_2)) {
        returnCode = eglBindAPI(EGL_OPENGL_ES_API);
    } else if (m_api == VG) {
        returnCode = eglBindAPI(EGL_OPENVG_API);
    }

    if (returnCode != EGL_TRUE) {
        eglPrintError("eglBindApi");
        return EXIT_FAILURE;
    }

    if(!eglChooseConfig(m_egl_disp, attrib_list, &m_egl_conf, 1, &num_configs)) {
        perror("eglChooseConfig");
        return EXIT_FAILURE;
    }

	setEGLInitialized(true);

    return EXIT_SUCCESS;
}

void
OpenGLThread::cleanupEGL() {
    //Typical EGL cleanup

    if (m_egl_disp != EGL_NO_DISPLAY) {
        eglTerminate(m_egl_disp);
        m_egl_disp = EGL_NO_DISPLAY;
    }

    if (m_egl_disp_hdmi != EGL_NO_DISPLAY) {
        eglTerminate(m_egl_disp_hdmi);
        m_egl_disp_hdmi = EGL_NO_DISPLAY;
    }

    eglReleaseThread();

    m_initialized = false;
}


void OpenGLThread::update() {
	int index, type;
	int viewCount = 0;
	int returnCode = 0;

	m_viewsMutex.lock();

	viewCount = m_views.size();

	m_viewsMutex.unlock();

	if (viewCount > 0) {
		m_viewsMutex.lock();

		int attached = 0;
		for (index = 0; index < m_numberDisplays; index++) {
	        screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_TYPE,  &type);
			attached = 0;

	        if (type == SCREEN_DISPLAY_TYPE_HDMI) {
				screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_ATTACHED, &attached);

				if (attached) {
					int size[2];
					screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_SIZE, size);
					if (size[0] == 0 || size[1] == 0) {
						attached = 0;
					}
				}
	        }
		}

		for(index = 0; index < m_views.size(); index++) {
			if (m_views.at(index)->display() == m_egl_disp_hdmi) {
				if (attached) {
					m_views.at(index)->setVisible(true);
				} else {
					m_views.at(index)->setVisible(false);
				}
			} else {
				m_views.at(index)->setVisible(true);
			}
		}

		for (int index = 0; index < m_views.size(); index++) {
			if (!m_views.at(index)->initialized() && m_views.at(index)->visible()) {

				m_views.at(index)->setAPI(m_api);
				m_views.at(index)->setScreenContext(m_screen_ctx);
				m_views.at(index)->setScreenEGLConfiguration(m_egl_conf);
				m_views.at(index)->setScreenEGLDisplay(m_egl_disp);
				returnCode = m_views.at(index)->initialize();
				if (returnCode == EXIT_FAILURE) {

					qDebug() << "OpenGLThread::update: view initialization failed:" << index;
				}
			}

			if (m_views.at(index)->altered() && m_views.at(index)->visible()) {
				returnCode = m_views.at(index)->regenerate();
				if (returnCode == EXIT_FAILURE) {

					qDebug() << "OpenGLThread::update: view regenerate failed:" << index;
				}
			}

			if (m_views.at(index)->initialized() && m_views.at(index)->enabled() && m_views.at(index)->visible()) {
				m_views.at(index)->update();
			}
		}

		m_viewsMutex.unlock();
	}
}

void OpenGLThread::cleanup() {
	int viewCount = 0;

	m_viewsMutex.lock();

	viewCount = m_views.size();

	m_viewsMutex.unlock();

	if (viewCount > 0) {
		do {
			removeView(m_views.at(m_views.size()-1));

			m_viewsMutex.lock();

			viewCount = m_views.size();

			m_viewsMutex.unlock();

		} while (viewCount > 0);
	}

	qDebug() << "OpenGLThread::cleanup";
}

void OpenGLThread::render() {
	int index, type;
	int viewCount = 0;

	m_viewsMutex.lock();

	viewCount = m_views.size();

	m_viewsMutex.unlock();

	if (viewCount > 0) {
		m_viewsMutex.lock();

		int attached = 0;
		for (index = 0; index < m_numberDisplays; index++) {
	        screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_TYPE,  &type);
			int attached = 0;

	        if (type == SCREEN_DISPLAY_TYPE_HDMI) {
				screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_ATTACHED, &attached);

				if (attached) {
					int size[2];
					screen_get_display_property_iv(m_screen_dpy[index], SCREEN_PROPERTY_SIZE, size);
					if (size[0] == 0 || size[1] == 0) {
						attached = 0;
					}
				}
	        }
		}

		for(index = 0; index < m_views.size(); index++) {
			if (m_views.at(index)->display() == m_egl_disp_hdmi) {
				if (attached) {
					m_views.at(index)->setVisible(true);
				} else {
					m_views.at(index)->setVisible(false);
				}
			} else {
				m_views.at(index)->setVisible(true);
			}
		}

		for (int index = 0; index < m_views.size(); index++) {
			if (m_views.at(index)->initialized() && m_views.at(index)->enabled() && m_views.at(index)->visible() && m_views.at(index)->stale()) {
				m_views.at(index)->renderView();
			}
		}

		m_viewsMutex.unlock();
	}
}


void OpenGLThread::handleScreenEvent(bps_event_t *event) {
    int buttons;
    int position[2];
    int index;
    screen_display_t event_disp;
    int type;

    screen_event_t screen_event = screen_event_get_event(event);

    //Query type of screen event and its location on the screen
    screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE,
            &type);
    screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION,
    		position);

	switch (type) {
		case SCREEN_EVENT_DISPLAY:
			screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_DISPLAY, (void **)&event_disp);
			for (index = 0; index < m_numberDisplays; index++) {
				if (event_disp == m_screen_dpy[index]) {
					int attached = 0;
					screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_ATTACHED, &attached);

					if (attached) {
						int size[2];
						screen_get_display_property_iv(event_disp, SCREEN_PROPERTY_SIZE, size);
						if (size[0] == 0 || size[1] == 0) {
							attached = 0;
						}
					}


					int viewCount = 0;

					m_viewsMutex.lock();

					viewCount = m_views.size();

					m_viewsMutex.unlock();

					if (viewCount > 0) {
						m_viewsMutex.lock();

						for(index = 0; index < m_views.size(); index++) {
							if (m_views.at(index)->display() == m_egl_disp_hdmi && index == 1) {
								if (attached) {
									m_views.at(index)->setVisible(true);
								} else {
									m_views.at(index)->setVisible(false);
								}
							}
						}

						m_viewsMutex.unlock();
					}
				}
			}
			break;
	}
}

void OpenGLThread::run()
{
	int index = 0;

	setStopped(false);

	qDebug() << "OpenGLThread started:" << !stopped();

	int returnCode = initBPS();

	if (returnCode == EXIT_SUCCESS && initialized() && eglInitialized()) {
		bps_event_t *event = NULL;

		while (!stopped()) {
			do {
				//Request and process BPS next available event
				event = NULL;
				returnCode = bps_get_event(&event, 0);
				//assert(rc == BPS_SUCCESS);

				if (event) {
					int domain = bps_event_get_domain(event);

					if (domain == screen_get_domain()) {
						handleScreenEvent(event);

						int viewCount = 0;

						m_viewsMutex.lock();

						viewCount = m_views.size();

						m_viewsMutex.unlock();

						if (viewCount > 0) {
							m_viewsMutex.lock();

							for(index = 0; index < m_views.size(); index++) {
								m_views.at(index)->handleScreenEvent(event);
							}

							m_viewsMutex.unlock();
						}
					}
				}
			} while (event);

			if (stopped()) {
				break;
			}

			//qDebug() << "OpenGLThread stopped:" << stopped();

			update();

			render();

			usleep(5);
		}

		// remove and cleanup each view
		cleanup();
	}

	//Stop requesting events from libscreen
	screen_stop_events(m_screen_ctx);

	//Shut down BPS library for this process
	bps_shutdown();

	setInitialized(false);

	//Use utility code to terminate EGL setup
	cleanupEGL();

	//Destroy libscreen context
	screen_destroy_context(m_screen_ctx);
}

void OpenGLThread::shutdown()
{
	setStopped(true);
}

EGLDisplay OpenGLThread::getDisplay(VIEW_DISPLAY display)
{
	EGLDisplay egl_display = EGL_NO_DISPLAY;

	while (!eglInitialized()) {
		usleep(1);
	};

	switch (display) {
		case DISPLAY_DEVICE:
			egl_display = m_egl_disp;
		break;

		case DISPLAY_HDMI:
			egl_display = m_egl_disp_hdmi;
		break;
	}

	return egl_display;
}

bool OpenGLThread::isDisplayAttached(VIEW_DISPLAY display)
{
	EGLDisplay egl_display = EGL_NO_DISPLAY;

	while (!eglInitialized()) {
		usleep(1);
	};

	switch (display) {
		case DISPLAY_DEVICE:
			egl_display = m_egl_disp;
		break;

		case DISPLAY_HDMI:
			egl_display = m_egl_disp_hdmi;
		break;
	}

	return egl_display;
}

OpenGLThread* OpenGLThread::getInstance()
{
	if (!singleton.isRunning() && !singleton.stopped()) {
		singleton.start();
	}

	return &singleton;
}

void OpenGLThread::eglPrintError(const char *msg) {
    static const char *errmsg[] = {
        "function succeeded",
        "EGL is not initialized, or could not be initialized, for the specified display",
        "cannot access a requested resource",
        "failed to allocate resources for the requested operation",
        "an unrecognized attribute or attribute value was passed in an attribute list",
        "an EGLConfig argument does not name a valid EGLConfig",
        "an EGLContext argument does not name a valid EGLContext",
        "the current surface of the calling thread is no longer valid",
        "an EGLDisplay argument does not name a valid EGLDisplay",
        "arguments are inconsistent",
        "an EGLNativePixmapType argument does not refer to a valid native pixmap",
        "an EGLNativeWindowType argument does not refer to a valid native window",
        "one or more argument values are invalid",
        "an EGLSurface argument does not name a valid surface configured for rendering",
        "a power management event has occurred",
    };

    fprintf(stderr, "%s: %s\n", msg, errmsg[eglGetError() - EGL_SUCCESS]);
}

Now that you're done

Now that you've completed the tutorial, you can look at the GoodCitizenCascades app.

Last modified: 2015-03-31



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus