Разработка на С++ для 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. Чтобы не заниматься поиском сокровищ, вот немного кастрированное их содержимое (но абсолютно достаточное для запуска нашего минипримера):

/*  * 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 
/*  * 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. У меня вот такая 150×150:
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 минут запустить готовое приложение.

ссылка на оригинал статьи http://habrahabr.ru/post/165523/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *