Разработка на С++ для BlackBerry 10 без использования IDE (с использованием OpenGL ES 2)

    Под BlackBerry OS 10 есть отличная среда разработки: QNX Momentics IDE, на базе Eclipse. И всё бы хорошо, но когда дело доходит до автоматизации сборки билдов, настройки билд-конфигураций на TeamCity, то IDE нужна как собаке пятая нога. Именно это было самой большой проблемой при портировании нашего движка на BlackBerry. Давайте разберёмся, как можно собрать проект, упаковать дистрибутив, подписать его и запустить на эмуляторе — и всё это без использования IDE.



    Зависимости



    Для сборки проекта нам понадобятся:



    C++ проект



    Начнём с того, что набросаем сырец нашего минималистичного приложения, которое будет рисовать цветной треугольник с помощью OpenGL ES 2.

    Это будет main.cpp.
    #include <assert.h>
    #include <screen/screen.h>
    #include <bps/navigator.h>
    #include <bps/screen.h>
    #include <bps/bps.h>
    #include <bps/event.h>
    #include <bps/locale.h>
    #include <bps/virtualkeyboard.h>
    #include <sys/keycodes.h>
    #include <input/event_types.h>
    #include <input/screen_helpers.h>
    #include <stdarg.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <EGL/egl.h>
    #include <GLES2/gl2.h>
    #include "bbutil.h"
    
    static const char g_vShaderStr[] =
    		"#version 100\n"
    	   "precision highp float;\n"
    	   "attribute vec3 vPosition;\n"
    	   "attribute vec3 vColor;\n"
    	   "varying vec4 Color;\n"
    	   "void main()\n"
    	   "{\n"
    	   "   Color = vec4( vColor, 1.0 );\n"
    	   "   gl_Position = vec4( vPosition, 1.0 );\n"
    	   "}\n";
    
    static const char g_fShaderStr[] =
    		"#version 100\n"
    	   "precision highp float;\n"
    	   "varying vec4 Color;\n"
    	   "void main()\n"
    	   "{\n"
    	   "   gl_FragColor = Color;\n"
    	   "}\n";
    
    static GLuint g_ProgramObject = 0;
    
    static GLuint LoadShader( GLenum type, const char* shaderSrc )
    {
    	GLuint shader = glCreateShader( type );
    	glShaderSource ( shader, 1, &shaderSrc, NULL );
    	glCompileShader ( shader );
    	GLint compiled;
    	glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
    	GLsizei MaxLength = 0;
    	glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &MaxLength );
    	char* InfoLog = new char[MaxLength];
    	glGetShaderInfoLog( shader, MaxLength, &MaxLength, InfoLog );
    	return shader;
    }
    
    static void GLDebug_LoadStaticProgramObject()
    {
    	if ( g_ProgramObject == 0 )
    	{
    		GLuint vertexShader = LoadShader ( GL_VERTEX_SHADER, g_vShaderStr );
    		GLuint fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, g_fShaderStr );
    		g_ProgramObject = glCreateProgram ( );
    		glAttachShader ( g_ProgramObject, vertexShader );
    		glAttachShader ( g_ProgramObject, fragmentShader );
    		glLinkProgram ( g_ProgramObject );
    		GLint linked;
    		glGetProgramiv ( g_ProgramObject, GL_LINK_STATUS, &linked );
    		GLsizei Length    = 0;
    		GLsizei MaxLength = 0;
    		glGetProgramiv( g_ProgramObject, GL_INFO_LOG_LENGTH, &MaxLength );
    		char* InfoLog = new char[MaxLength];
    		glGetProgramInfoLog( g_ProgramObject, MaxLength, &Length, InfoLog );
    	}
    }
    
    static void GLDebug_RenderTriangle()
    {
    	const GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f };
    	const GLfloat vColors[]   = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
    
    	glUseProgram ( g_ProgramObject );
    	GLint Loc1 = glGetAttribLocation( g_ProgramObject, "vPosition" );
    	GLint Loc2 = glGetAttribLocation( g_ProgramObject, "vColor" );
    
    	glBindBuffer( GL_ARRAY_BUFFER, 0 );
    	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
    	glVertexAttribPointer ( Loc1, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
    	glVertexAttribPointer ( Loc2, 3, GL_FLOAT, GL_FALSE, 0, vColors   );
    	glEnableVertexAttribArray ( Loc1 );
    	glEnableVertexAttribArray ( Loc2 );
    
    	//glUseProgram ( g_ProgramObject );
    	glDisable( GL_DEPTH_TEST );
    	glDrawArrays ( GL_TRIANGLES, 0, 3 );
    	glUseProgram ( 0 );
    	glDisableVertexAttribArray ( Loc1 );
    	glDisableVertexAttribArray ( Loc2 );
    }
    
    static screen_context_t screen_cxt;
    
    void handleScreenEvent( bps_event_t* event )
    {
    	screen_event_t screen_event = screen_event_get_event( event );
    
    	// handle clicks, touches, keypresses
    }
    
    void handleNavigatorEvent( bps_event_t* event )
    {
    	int rc;
    	bps_event_t* activation_event = NULL;
    
    	switch ( bps_event_get_code( event ) )
    	{
    		case NAVIGATOR_ORIENTATION_CHECK:
    			// signal navigator that we do not intend to resize
    			navigator_orientation_check_response( event, false );
    			break;
    	}
    }
    
    int main( int argc, char* argv[] )
    {
    	int rc;
    	int exit_application = 0;
    
    	screen_create_context( &screen_cxt, 0 );
    
    	bps_initialize();
    
    	//Use utility code to initialize EGL for rendering with GL ES 2.0
    	if ( EXIT_SUCCESS != bbutil_init_egl( screen_cxt ) )
    	{
    		fprintf( stderr, "bbutil_init_egl failed\n" );
    		bbutil_terminate();
    		screen_destroy_context( screen_cxt );
    		return 0;
    	}
    
    	// Signal BPS library that navigator and screen events will be requested
    	if ( BPS_SUCCESS != screen_request_events( screen_cxt ) )
    	{
    		fprintf( stderr, "screen_request_events failed\n" );
    		bbutil_terminate();
    		screen_destroy_context( screen_cxt );
    		bps_shutdown();
    		return 0;
    	}
    
    	if ( BPS_SUCCESS != navigator_request_events( 0 ) )
    	{
    		fprintf( stderr, "navigator_request_events failed\n" );
    		bbutil_terminate();
    		screen_destroy_context( screen_cxt );
    		bps_shutdown();
    		return 0;
    	}
    
    	if ( BPS_SUCCESS != virtualkeyboard_request_events( 0 ) )
    	{
    		fprintf( stderr, "virtualkeyboard_request_events failed\n" );
    		bbutil_terminate();
    		screen_destroy_context( screen_cxt );
    		bps_shutdown();
    		return 0;
    	}
    
    	// Signal BPS library that navigator orientation is not to be locked
    	if ( BPS_SUCCESS != navigator_rotation_lock( false ) )
    	{
    		fprintf( stderr, "navigator_rotation_lock failed\n" );
    		bbutil_terminate();
    		screen_destroy_context( screen_cxt );
    		bps_shutdown();
    		return 0;
    	}
    
    	// Query width and height of the window surface created by utility code
    	EGLint surface_width, surface_height;
    	eglQuerySurface( egl_disp, egl_surf, EGL_WIDTH, &surface_width );
    	eglQuerySurface( egl_disp, egl_surf, EGL_HEIGHT, &surface_height );
    
    	GLDebug_LoadStaticProgramObject();
    
    	while ( !exit_application )
    	{
    		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
    		// Request and process all available BPS events
    		bps_event_t* event = NULL;
    
    		for ( ;; )
    		{
    			rc = 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 );
    				}
    				else if ( domain == navigator_get_domain() )
    				{
    					handleNavigatorEvent( event );
    					if ( NAVIGATOR_EXIT == bps_event_get_code( event ) ) { exit_application = 1; }
    				}
    			}
    			else break;
    		}
    
    		// render frame here
    		GLDebug_RenderTriangle();
    
    		bbutil_swap();
    	}
    
    	// Stop requesting events from libscreen
    	screen_stop_events( screen_cxt );
    
    	// Shut down BPS library for this process
    	bps_shutdown();
    
    	// Use utility code to terminate EGL setup
    	bbutil_terminate();
    
    	// Destroy libscreen context
    	screen_destroy_context( screen_cxt );
    
    	return 0;
    }
    



    Ещё нам понадобятся bbutil.c и bbutil.h из примеров BlackBerry 10 Native SDK. Чтобы не заниматься поиском сокровищ, вот немного кастрированное их содержимое (но абсолютно достаточное для запуска нашего минипримера).

    bbutil.h
    /*
     * 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 _UTILITY_H_INCLUDED
    #define _UTILITY_H_INCLUDED
    
    #include <EGL/egl.h>
    #include <screen/screen.h>
    #include <sys/platform.h>
    
    #define USING_GL20
    
    extern EGLDisplay egl_disp;
    extern EGLSurface egl_surf;
    
    typedef struct font_t font_t;
    
    #define BBUTIL_DEFAULT_FONT "/usr/fonts/font_repository/monotype/arial.ttf"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    	/**
    	 * Initializes EGL
    	 *
    	 * @param libscreen context that will be used for EGL setup
    	 * @return EXIT_SUCCESS if initialization succeeded otherwise EXIT_FAILURE
    	 */
    	int bbutil_init_egl( screen_context_t ctx );
    
    	/**
    	 * Terminates EGL
    	 */
    	void bbutil_terminate();
    
    	/**
    	 * Swaps default bbutil window surface to the screen
    	 */
    	void bbutil_swap();
    
    	/**
    	 * Returns dpi for a given screen
    
    	 *
    	 * @param ctx path libscreen context that corresponds to display of interest
    	 * @return dpi for a given screen
    	 */
    
    	int bbutil_calculate_dpi( screen_context_t ctx );
    
    	/**
    	 * Rotates the screen to a given angle
    
    	 *
    	 * @param angle to rotate screen surface to, must by 0, 90, 180, or 270
    	 * @return EXIT_SUCCESS if texture loading succeeded otherwise EXIT_FAILURE
    	 */
    
    	int bbutil_rotate_screen_surface( int angle );
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    



    bbutil.c
    /*
     * 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 <assert.h>
    #include <ctype.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/keycodes.h>
    #include <time.h>
    #include <stdbool.h>
    #include <math.h>
    
    #include "bbutil.h"
    
    #ifdef USING_GL11
    #include <GLES/gl.h>
    #include <GLES/glext.h>
    #elif defined(USING_GL20)
    #include <GLES2/gl2.h>
    #else
    #error bbutil must be compiled with either USING_GL11 or USING_GL20 flags
    #endif
    
    EGLDisplay egl_disp;
    EGLSurface egl_surf;
    
    static EGLConfig egl_conf;
    static EGLContext egl_ctx;
    
    static screen_context_t screen_ctx;
    static screen_window_t screen_win;
    static screen_display_t screen_disp;
    static int nbuffers = 2;
    static int initialized = 0;
    
    #ifdef USING_GL20
    static GLuint text_rendering_program;
    static int text_program_initialized = 0;
    static GLint positionLoc;
    static GLint texcoordLoc;
    static GLint textureLoc;
    static GLint colorLoc;
    #endif
    
    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;
    };
    
    
    static void
    bbutil_egl_perror( 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",
    		"unknown error code"
    	};
    
    	int message_index = eglGetError() - EGL_SUCCESS;
    
    	if ( message_index < 0 || message_index > 14 )
    	{
    		message_index = 15;
    	}
    
    	fprintf( stderr, "%s: %s\n", msg, errmsg[message_index] );
    }
    
    int
    bbutil_init_egl( screen_context_t ctx )
    {
    	int usage;
    	int format = SCREEN_FORMAT_RGBX8888;
    	EGLint interval = 1;
    	int rc, num_configs;
    
    	EGLint attrib_list[] = { EGL_RED_SIZE,        8,
    	                         EGL_GREEN_SIZE,      8,
    	                         EGL_BLUE_SIZE,       8,
    	                         EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
    	                         EGL_RENDERABLE_TYPE, 0,
    	                         EGL_NONE
    	                       };
    
    #ifdef USING_GL11
    	usage = SCREEN_USAGE_OPENGL_ES1 | SCREEN_USAGE_ROTATION;
    	attrib_list[9] = EGL_OPENGL_ES_BIT;
    #elif defined(USING_GL20)
    	usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_ROTATION;
    	attrib_list[9] = EGL_OPENGL_ES2_BIT;
    	EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
    #else
    	fprintf( stderr, "bbutil should be compiled with either USING_GL11 or USING_GL20 -D flags\n" );
    	return EXIT_FAILURE;
    #endif
    
    	//Simple egl initialization
    	screen_ctx = ctx;
    
    	egl_disp = eglGetDisplay( EGL_DEFAULT_DISPLAY );
    
    	if ( egl_disp == EGL_NO_DISPLAY )
    	{
    		bbutil_egl_perror( "eglGetDisplay" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = eglInitialize( egl_disp, NULL, NULL );
    
    	if ( rc != EGL_TRUE )
    	{
    		bbutil_egl_perror( "eglInitialize" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = eglBindAPI( EGL_OPENGL_ES_API );
    
    	if ( rc != EGL_TRUE )
    	{
    		bbutil_egl_perror( "eglBindApi" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	if ( !eglChooseConfig( egl_disp, attrib_list, &egl_conf, 1, &num_configs ) )
    	{
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    #ifdef USING_GL20
    	egl_ctx = eglCreateContext( egl_disp, egl_conf, EGL_NO_CONTEXT, attributes );
    #elif defined(USING_GL11)
    	egl_ctx = eglCreateContext( egl_disp, egl_conf, EGL_NO_CONTEXT, NULL );
    #endif
    
    	if ( egl_ctx == EGL_NO_CONTEXT )
    	{
    		bbutil_egl_perror( "eglCreateContext" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_create_window( &screen_win, screen_ctx );
    
    	if ( rc )
    	{
    		perror( "screen_create_window" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_set_window_property_iv( screen_win, SCREEN_PROPERTY_FORMAT, &format );
    
    	if ( rc )
    	{
    		perror( "screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_set_window_property_iv( screen_win, SCREEN_PROPERTY_USAGE, &usage );
    
    	if ( rc )
    	{
    		perror( "screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_get_window_property_pv( screen_win, SCREEN_PROPERTY_DISPLAY, ( void** )&screen_disp );
    
    	if ( rc )
    	{
    		perror( "screen_get_window_property_pv" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	int screen_resolution[2];
    
    	rc = screen_get_display_property_iv( screen_disp, SCREEN_PROPERTY_SIZE, screen_resolution );
    
    	if ( rc )
    	{
    		perror( "screen_get_display_property_iv" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	int angle = atoi( getenv( "ORIENTATION" ) );
    
    	screen_display_mode_t screen_mode;
    	rc = screen_get_display_property_pv( screen_disp, SCREEN_PROPERTY_MODE, ( void** )&screen_mode );
    
    	if ( rc )
    	{
    		perror( "screen_get_display_property_pv" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	int size[2];
    	rc = screen_get_window_property_iv( screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size );
    
    	if ( rc )
    	{
    		perror( "screen_get_window_property_iv" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	int buffer_size[2] = {size[0], size[1]};
    
    	if ( ( angle == 0 ) || ( angle == 180 ) )
    	{
    		if ( ( ( screen_mode.width > screen_mode.height ) && ( size[0] < size[1] ) ) ||
    		     ( ( screen_mode.width < screen_mode.height ) && ( size[0] > size[1] ) ) )
    		{
    			buffer_size[1] = size[0];
    			buffer_size[0] = size[1];
    		}
    	}
    	else if ( ( angle == 90 ) || ( angle == 270 ) )
    	{
    		if ( ( ( screen_mode.width > screen_mode.height ) && ( size[0] > size[1] ) ) ||
    		     ( ( screen_mode.width < screen_mode.height && size[0] < size[1] ) ) )
    		{
    			buffer_size[1] = size[0];
    			buffer_size[0] = size[1];
    		}
    	}
    	else
    	{
    		fprintf( stderr, "Navigator returned an unexpected orientation angle.\n" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_set_window_property_iv( screen_win, SCREEN_PROPERTY_BUFFER_SIZE, buffer_size );
    
    	if ( rc )
    	{
    		perror( "screen_set_window_property_iv" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_set_window_property_iv( screen_win, SCREEN_PROPERTY_ROTATION, &angle );
    
    	if ( rc )
    	{
    		perror( "screen_set_window_property_iv" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_create_window_buffers( screen_win, nbuffers );
    
    	if ( rc )
    	{
    		perror( "screen_create_window_buffers" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	egl_surf = eglCreateWindowSurface( egl_disp, egl_conf, screen_win, NULL );
    
    	if ( egl_surf == EGL_NO_SURFACE )
    	{
    		bbutil_egl_perror( "eglCreateWindowSurface" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = eglMakeCurrent( egl_disp, egl_surf, egl_surf, egl_ctx );
    
    	if ( rc != EGL_TRUE )
    	{
    		bbutil_egl_perror( "eglMakeCurrent" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	rc = eglSwapInterval( egl_disp, interval );
    
    	if ( rc != EGL_TRUE )
    	{
    		bbutil_egl_perror( "eglSwapInterval" );
    		bbutil_terminate();
    		return EXIT_FAILURE;
    	}
    
    	initialized = 1;
    
    	return EXIT_SUCCESS;
    }
    
    void
    bbutil_terminate()
    {
    	//Typical EGL cleanup
    	if ( egl_disp != EGL_NO_DISPLAY )
    	{
    		eglMakeCurrent( egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
    
    		if ( egl_surf != EGL_NO_SURFACE )
    		{
    			eglDestroySurface( egl_disp, egl_surf );
    			egl_surf = EGL_NO_SURFACE;
    		}
    
    		if ( egl_ctx != EGL_NO_CONTEXT )
    		{
    			eglDestroyContext( egl_disp, egl_ctx );
    			egl_ctx = EGL_NO_CONTEXT;
    		}
    
    		if ( screen_win != NULL )
    		{
    			screen_destroy_window( screen_win );
    			screen_win = NULL;
    		}
    
    		eglTerminate( egl_disp );
    		egl_disp = EGL_NO_DISPLAY;
    	}
    
    	eglReleaseThread();
    
    	initialized = 0;
    }
    
    void
    bbutil_swap()
    {
    	int rc = eglSwapBuffers( egl_disp, egl_surf );
    
    	if ( rc != EGL_TRUE )
    	{
    		bbutil_egl_perror( "eglSwapBuffers" );
    	}
    }
    
    /* Finds the next power of 2 */
    static inline int
    nextp2( int x )
    {
    	int val = 1;
    
    	while ( val < x ) { val <<= 1; }
    
    	return val;
    }
    
    int bbutil_calculate_dpi( screen_context_t ctx )
    {
    	int rc;
    	int screen_phys_size[2];
    
    	rc = screen_get_display_property_iv( screen_disp, SCREEN_PROPERTY_PHYSICAL_SIZE, screen_phys_size );
    
    	if ( rc )
    	{
    		perror( "screen_get_display_property_iv" );
    		bbutil_terminate();
    		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];
    		rc = screen_get_display_property_iv( screen_disp, SCREEN_PROPERTY_SIZE, screen_resolution );
    
    		if ( rc )
    		{
    			perror( "screen_get_display_property_iv" );
    			bbutil_terminate();
    			return EXIT_FAILURE;
    		}
    
    		double diagonal_pixels = sqrt( screen_resolution[0] * screen_resolution[0] + screen_resolution[1] * screen_resolution[1] );
    		double diagonal_inches = 0.0393700787 * sqrt( screen_phys_size[0] * screen_phys_size[0] + screen_phys_size[1] * screen_phys_size[1] );
    		return ( int )( diagonal_pixels / diagonal_inches + 0.5 );
    
    	}
    }
    
    int bbutil_rotate_screen_surface( int angle )
    {
    	int rc, rotation, skip = 1, temp;;
    	EGLint interval = 1;
    	int size[2];
    
    	if ( ( angle != 0 ) && ( angle != 90 ) && ( angle != 180 ) && ( angle != 270 ) )
    	{
    		fprintf( stderr, "Invalid angle\n" );
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_get_window_property_iv( screen_win, SCREEN_PROPERTY_ROTATION, &rotation );
    
    	if ( rc )
    	{
    		perror( "screen_set_window_property_iv" );
    		return EXIT_FAILURE;
    	}
    
    	rc = screen_get_window_property_iv( 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;
    	}
    
    	if ( !skip )
    	{
    		rc = eglMakeCurrent( egl_disp, NULL, NULL, NULL );
    
    		if ( rc != EGL_TRUE )
    		{
    			bbutil_egl_perror( "eglMakeCurrent" );
    			return EXIT_FAILURE;
    		}
    
    		rc = eglDestroySurface( egl_disp, egl_surf );
    
    		if ( rc != EGL_TRUE )
    		{
    			bbutil_egl_perror( "eglMakeCurrent" );
    			return EXIT_FAILURE;
    		}
    
    		rc = screen_set_window_property_iv( screen_win, SCREEN_PROPERTY_SOURCE_SIZE, size );
    
    		if ( rc )
    		{
    			perror( "screen_set_window_property_iv" );
    			return EXIT_FAILURE;
    		}
    
    		rc = screen_set_window_property_iv( screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size );
    
    		if ( rc )
    		{
    			perror( "screen_set_window_property_iv" );
    			return EXIT_FAILURE;
    		}
    
    		egl_surf = eglCreateWindowSurface( egl_disp, egl_conf, screen_win, NULL );
    
    		if ( egl_surf == EGL_NO_SURFACE )
    		{
    			bbutil_egl_perror( "eglCreateWindowSurface" );
    			return EXIT_FAILURE;
    		}
    
    		rc = eglMakeCurrent( egl_disp, egl_surf, egl_surf, egl_ctx );
    
    		if ( rc != EGL_TRUE )
    		{
    			bbutil_egl_perror( "eglMakeCurrent" );
    			return EXIT_FAILURE;
    		}
    
    		rc = eglSwapInterval( egl_disp, interval );
    
    		if ( rc != EGL_TRUE )
    		{
    			bbutil_egl_perror( "eglSwapInterval" );
    			return EXIT_FAILURE;
    		}
    	}
    
    	rc = screen_set_window_property_iv( screen_win, SCREEN_PROPERTY_ROTATION, &angle );
    
    	if ( rc )
    	{
    		perror( "screen_set_window_property_iv" );
    		return EXIT_FAILURE;
    	}
    
    	return EXIT_SUCCESS;
    }
    



    Сборка проекта



    Внутри у BlackBerry 10 находится QNX и сборка под неё осуществляется с помощью gcc. Напишем makefile:

    BBARCH=x86
    CC  = i486-pc-nto-qnx8.0.0-gcc.exe
    CXX = i486-pc-nto-qnx8.0.0-gcc.exe
    AR  = i486-pc-nto-qnx8.0.0-ar.exe
    
    OBJDIR=out
    OUTDIR=out
    
    OBJS = $(OBJDIR)/main.o $(OBJDIR)/bbutil.o
    
    $(OBJDIR)/bbutil.o: bbutil.c bbutil.h
    	$(CC) -c bbutil.c -o $(OBJDIR)/bbutil.o $(CFLAGS)
    
    $(OBJDIR)/main.o: main.cpp
    	$(CXX) -c main.cpp -o $(OBJDIR)/main.o $(CFLAGS) $(CPPFLAGS)
    
    OBJS += $(OBJDIR)/bbutil.o $(OBJDIR)/main.o
    
    USEDLIBS = -lstdc++ -lbps -lscreen -lm -lEGL -lGLESv2 -lfreetype -lsocket -lcurl -lOpenAL
    CFLAGS   = -m32 -fomit-frame-pointer
    CPPFLAGS = -std=gnu++0x -fpermissive
    
    PACK: $(OBJS)
    	rm -f $(OBJDIR)/pack.a
    	ar -ru $(OBJDIR)/pack.a $(OBJS)
    
    all: $(OBJS) PACK
    	$(CXX) $(CFLAGS) $(CPPFLAGS) -o Main.exe-$(BBARCH) main.cpp $(OBJDIR)/pack.a $(USEDLIBS)
    


    Не забудем создать папку out для временных файлов.

    Теперь бинарник проекта уже почти можно собрать командой make all, нужно только добавить необходимые для BlackBerry 10 Native SDK переменные окружения. Поскольку возможных таргетов несколько (девайс на arm и эмулятор на x86), то мы решили создать простой скрипт на питоне, который и будет запускать сборку. Вот make-x86.py:

    #!/usr/bin/python
    import os
    Target = "M:/BBNDK/target_10_0_9_1673/qnx6"
    Host   = "M:/BBNDK/host_10_0_9_404/win32/x86"
    os.environ['QNX_TARGET'] = Target
    os.environ['QNX_HOST'  ] = Host
    os.environ['PATH'      ] = Host + "/usr/bin;" + os.environ['PATH']
    os.system( "make all" )
    


    Запускаем make-x86.py и получаем бинарник Main.exe-x86.

    Сборка пакета



    Теперь нужно создать пакет, пригодный к установке на BlackBerry 10. Запишем описание пакета в bar-descriptor-x86.xml

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <qnx xmlns="http://www.qnx.com/schemas/application/1.0">
        <id>com.linderdaum.test_app</id>
        <name>Test App</name>
        <versionNumber>1.0.0</versionNumber>
        <buildId>1</buildId>
        <description>Test App</description>
        <author>Linderdaum</author>
        <platformVersion>10.0.9.1673</platformVersion>
        <initialWindow>
            <systemChrome>none</systemChrome>
            <transparent>false</transparent>
            <aspectRatio>landscape</aspectRatio>
            <autoOrients>false</autoOrients>
        </initialWindow>
        <category>core.games</category>
        <asset path="BB10Res/icon.png">icon.png</asset>
        <configuration id="com.qnx.qcc.toolChain.1246487324" name="Simulator-Debug">
           <platformArchitecture>x86</platformArchitecture>
           <asset path="Main.exe-x86" entry="true" type="Qnx/Elf">Main.exe-x86</asset>
        </configuration>
        <icon>
           <image>icon.png</image>
        </icon>
        <permission system="true">run_native</permission>
        <env var="LD_LIBRARY_PATH" value="app/native/lib"/>
    </qnx>
    


    и не забудем положить подходящую иконку в BB10Res/icon.png. У меня вот такая 150x150:
    image

    Упаковываем всё в Main-x86.bar:

    blackberry-nativepackager -devMode -package Main-x86.bar bar-descriptor-x86.xml
    


    Установка и запуск



    Осталось только установить на эмулятор:

    blackberry-deploy -installApp -device 192.168.70.130 Main-x86.bar
    


    image

    И запустить:

    image

    P.S. Для того, чтобы загрузить приложение на BlackBerry App World, его нужно подписать. Из командной строки это делается одной командой:

    blackberry-signer -storepass <пароль от хранилища подписи> Main-armv7.bar
    


    P.P.S. Сорри, что так много кода. Зато вы можете скопи-пастить всё это и за 15 минут запустить готовое приложение.
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 3

      0
      Подскажите, пожалуйста:
      Ваш main.cpp включает файл <screen/screen.h>, а в Makefile есть ключ -lscreen.
      Откуда Вы взяли screen.h и libscreen.so, это часть Blackberry Native SDK?
      Если да, то как скачать SDK?

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое