Под BlackBerry OS 10 есть отличная среда разработки: QNX Momentics IDE, на базе Eclipse. И всё бы хорошо, но когда дело доходит до автоматизации сборки билдов, настройки билд-конфигураций на TeamCity, то IDE нужна как собаке пятая нога. Именно это было самой большой проблемой при портировании нашего движка на BlackBerry. Давайте разбирёмся, как можно собрать проект, упаковать дистрибутив, подписать его и запустить на эмуляторе — и всё это без использования IDE.
Зависимости
Для сборки проекта нам понадобятся:
- BlackBerry 10 Native SDK и эмулятор для тестов: developer.blackberry.com/native/download/
- Python: python.org/download
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 100n"
"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 100n"
"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 failedn" );
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 failedn" );
bbutil_terminate();
screen_destroy_context( screen_cxt );
bps_shutdown();
return 0;
}
if ( BPS_SUCCESS != navigator_request_events( 0 ) )
{
fprintf( stderr, "navigator_request_events failedn" );
bbutil_terminate();
screen_destroy_context( screen_cxt );
bps_shutdown();
return 0;
}
if ( BPS_SUCCESS != virtualkeyboard_request_events( 0 ) )
{
fprintf( stderr, "virtualkeyboard_request_events failedn" );
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 failedn" );
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: %sn", 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 flagsn" );
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 anglen" );
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:
Упаковываем всё в 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
И запустить:
P.S. Для того, чтобы загрузить приложение на BlackBerry App World, его нужно подписать. Из командной строки это делается одной командой:
blackberry-signer -storepass <пароль от хранилища подписи> Main-armv7.bar
P.P.S. Сорри, что так много кода. Зато вы можете скопи-пастить всё это и за 15 минут запустить готовое приложение.
Автор: CorporateShark