Initialize the application

Before we can display anything on the screen, we need to do a bit of initialization. We'll initialize the screen so that we can draw on it, and we'll also set up some initial data for the application.

In the main.c file, in main(), create a screen context. We use this context to initialize EGL for using a window surface. EGL is an interface between standard rendering APIs (like OpenGL ES) and the underlying windowing system. We also use this context to receive screen events.
screen_create_context(&screen_cxt, 0);
Initialize the BlackBerry Platform Services library. This allows our application to receive and process events. You must perform this step before calling any other BlackBerry Platform Services functions.
Initialize EGL using the screen context that we created. To do this, we use a utility function called bbutil_init_egl(). This function is part of bbutil.h, which was included automatically when we created the Good Citizen project. The bbutil.h file is a collection of functions that help us set up and work with EGL. These functions perform all of the EGL initialization and manipulation behind the scenes, so we can focus on the logic of our application. You can take a look at bbutil_init_egl() to see how you can initialize a native surface. If the EGL initialization doesn't succeed, we print an error to stderr, clean up the EGL surface by calling bbutil_terminate() (which is another utility function in bbutil.h), destroy the screen context, and close the application.
if (EXIT_SUCCESS != bbutil_init_egl(screen_cxt)) {
    fprintf(stderr, "bbutil_init_egl failed\n");
    return 0;
Initialize the application data. We call initialize() to perform this initialization. If the initialization doesn't succeed, we clean up and close the application.
if (EXIT_SUCCESS != initialize()) {
    fprintf(stderr, "initialize failed\n");
    return 0;
In initialize(), we load the texture for the unselected radio button from a .png file, using bbutil_load_texture().
float tex_x = 1.0f, tex_y = 1.0f;
int size_x = 64, size_y = 64;

        != bbutil_load_texture("app/native/radio_btn_unselected.png",
                NULL, NULL, &tex_x, &tex_y, &radio_btn_unselected)) {
    fprintf(stderr, "Unable to load non-selected radio button texture\n");
Define the vertices of the unselected radio button to which the texture is applied.
radio_btn_unselected_vertices[0] = 0.0f;
radio_btn_unselected_vertices[1] = 0.0f;
radio_btn_unselected_vertices[2] = size_x;
radio_btn_unselected_vertices[3] = 0.0f;
radio_btn_unselected_vertices[4] = 0.0f;
radio_btn_unselected_vertices[5] = size_y;
radio_btn_unselected_vertices[6] = size_x;
radio_btn_unselected_vertices[7] = size_y;
Define the coordinates of the unselected radio button to which the texture is applied.
radio_btn_unselected_tex_coord[0] = 0.0f;
radio_btn_unselected_tex_coord[1] = 0.0f;
radio_btn_unselected_tex_coord[2] = tex_x;
radio_btn_unselected_tex_coord[3] = 0.0f;
radio_btn_unselected_tex_coord[4] = 0.0f;
radio_btn_unselected_tex_coord[5] = tex_y;
radio_btn_unselected_tex_coord[6] = tex_x;
radio_btn_unselected_tex_coord[7] = tex_y;
Repeat these definitions for the selected radio button.
  != bbutil_load_texture("app/native/radio_btn_selected.png", NULL,
  NULL, &tex_x, &tex_y, &radio_btn_selected)) {
    fprintf(stderr, "Unable to load selected radio button texture\n");

radio_btn_selected_vertices[0] = 0.0f;
radio_btn_selected_vertices[1] = 0.0f;
radio_btn_selected_vertices[2] = size_x;
radio_btn_selected_vertices[3] = 0.0f;
radio_btn_selected_vertices[4] = 0.0f;
radio_btn_selected_vertices[5] = size_y;
radio_btn_selected_vertices[6] = size_x;
radio_btn_selected_vertices[7] = size_y;

radio_btn_selected_tex_coord[0] = 0.0f;
radio_btn_selected_tex_coord[1] = 0.0f;
radio_btn_selected_tex_coord[2] = tex_x;
radio_btn_selected_tex_coord[3] = 0.0f;
radio_btn_selected_tex_coord[4] = 0.0f;
radio_btn_selected_tex_coord[5] = tex_y;
radio_btn_selected_tex_coord[6] = tex_x;
radio_btn_selected_tex_coord[7] = tex_y;

//Define the size of the radio buttons
button_size_x = (float) size_x;
button_size_y = (float) size_y;
Load the background texture, which includes a shadow for the cube, and then specify its size and coordinates to match the dimensions of the EGL surface. This is done for both landscape and portrait orientations.
eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width);
eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height);

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

width = (float) surface_width;
height = (float) surface_height;

        != bbutil_load_texture("app/native/background-landscape.png", NULL, NULL,
            &tex_x, &tex_y, &background_landscape)) {
    fprintf(stderr, "Unable to load landscape background texture\n");

size_x = (width > height) ? width : height;
size_y = (width > height) ? height : width;

background_landscape_vertices[0] = 0.0f;
background_landscape_vertices[1] = 0.0f;
background_landscape_vertices[2] = size_x;
background_landscape_vertices[3] = 0.0f;
background_landscape_vertices[4] = 0.0f;
background_landscape_vertices[5] = size_y;
background_landscape_vertices[6] = size_x;
background_landscape_vertices[7] = size_y;

background_landscape_tex_coord[0] = 0.0f;
background_landscape_tex_coord[1] = 0.0f;
background_landscape_tex_coord[2] = tex_x;
background_landscape_tex_coord[3] = 0.0f;
background_landscape_tex_coord[4] = 0.0f;
background_landscape_tex_coord[5] = tex_y;
background_landscape_tex_coord[6] = tex_x;
background_landscape_tex_coord[7] = tex_y;

        != bbutil_load_texture("app/native/background-portrait.png", NULL, NULL,
            &tex_x, &tex_y, &background_portrait)) {
    fprintf(stderr, "Unable to load portrait background texture\n");

size_x = (height > width) ? width : height;
size_y = (height > width) ? height : width;

background_portrait_vertices[0] = 0.0f;
background_portrait_vertices[1] = 0.0f;
background_portrait_vertices[2] = size_x;
background_portrait_vertices[3] = 0.0f;
background_portrait_vertices[4] = 0.0f;
background_portrait_vertices[5] = size_y;
background_portrait_vertices[6] = size_x;
background_portrait_vertices[7] = size_y;

background_portrait_tex_coord[0] = 0.0f;
background_portrait_tex_coord[1] = 0.0f;
background_portrait_tex_coord[2] = tex_x;
background_portrait_tex_coord[3] = 0.0f;
background_portrait_tex_coord[4] = 0.0f;
background_portrait_tex_coord[5] = tex_y;
background_portrait_tex_coord[6] = tex_x;
background_portrait_tex_coord[7] = tex_y;
Initialize some variables, which will be used later, to zero.
angle = 0.0f;
pos_x = 0.0f;
pos_y = 0.0f;
Load the font type to use for our color menu and then determine the height of the menu for use later when we're determining which menu button is pressed. The function bbutil_load_font() generates the font symbols at the appropriate size for the screen of the BlackBerry 10 device through the use of dpi (dots per inch). We also need to calculate the point size for the font, which is specific to the device, using the font size (15 point) and the dpi of a BlackBerry PlayBook (170) as a reference value.
int dpi = bbutil_calculate_dpi(screen_cxt);

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

int point_size = (int)(15.0f / ((float)dpi / 170.0f ));

font = bbutil_load_font("/usr/fonts/font_repository/monotype/arial.ttf", point_size, dpi);
if (!font) {
    return EXIT_FAILURE;

float text_width, text_height;
bbutil_measure_text(font, "Color Menu", &text_width, &text_height);
menu_height = text_height + 10.0f + button_size_y * 4;
A save file is used to save information about whether or not the menu is displayed (menu_active) and which color is chosen (selected). This save file is used to restore these two items if the application is closed and then started again. Determine whether a save file exists. If it doesn't, initialize the application so that the menu is hidden and the cube color is red.
if (!read_from_file()) {
    selected = 3;
    cube_color[0] = 1.0f;
    cube_color[1] = 0.0f;
    cube_color[2] = 0.0f;
    cube_color[3] = 1.0f;

    menu_animation = 0.0f;
    menu_active = false;
    menu_show_animation = false;
    menu_hide_animation = false;
Stepping briefly into the read_from_file() function, which is used to read saved data from disk, open the saved file as a binary object.
FILE *fp = fopen("data/save.dat", "rb");
if (!fp) {
    return false;

It's important to note that an application must use the data folder for files that are used for reading and writing. If you are using files that are deployed with the application, they must be copied from the deployment location, app/native, to data in order to write to them.

If there is a saved file in memory, load this file with all of the required specifications. The specifications include the color of the cube and whether the menu is active.
int rc = fscanf(fp, "%i %i", &selected, &menu_active);

if (rc == -1) {
    return false;
} else {
    if (selected == 0) {
        cube_color[0] = 1.0f;
        cube_color[1] = 1.0f;
        cube_color[2] = 0.0f;
        cube_color[3] = 1.0f;
    } else if (selected == 1) {
        cube_color[0] = 0.0f;
        cube_color[1] = 0.0f;
        cube_color[2] = 1.0f;
        cube_color[3] = 1.0f;
    } else if (selected == 2) {
        cube_color[0] = 0.0f;
        cube_color[1] = 1.0f;
        cube_color[2] = 0.0f;
        cube_color[3] = 1.0f;
    } else if (selected == 3) {
        cube_color[0] = 1.0f;
        cube_color[1] = 0.0f;
        cube_color[2] = 0.0f;
        cube_color[3] = 1.0f;
    } else {
        return false;

//If the menu was saved in the active state, display it
if (menu_active) {
    menu_animation = menu_height;

Back in initialize(), initialize the positions of assets on the screen with respect to the current size of our surface. Note that this function is used during orientation change handling later on and is explained later.
if (EXIT_FAILURE == resize(NULL)) {
    fprintf(stderr, "Initialize surface\n");
    return EXIT_FAILURE;
We specify common OpenGL parameters, such as smooth shading, setting the type and position of the light source in the scene, and enabling polygon culling.
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_direction);


menu_show_animation = true; 

Back in main(), request events from the screen and the navigator so that the application can process them. We're interested in receiving screen events (touch events to interact with the menu) and navigator events (swipe down events to toggle the menu, navigator orientation events, and navigator window active/inactive events). When we request screen and navigator events, we verify that the requests were successful. If not, we clean up and close the application.
if (BPS_SUCCESS != screen_request_events(screen_cxt)) {
    fprintf(stderr, "screen_request_events failed\n");
    return 0;

if (BPS_SUCCESS != navigator_request_events(0)) {
    fprintf(stderr, "navigator_request_events failed\n");
    return 0;
Indicate to the BlackBerry Platform Services that we are interested in receiving and processing navigator orientation events. This setting, in addition to the orientation settings in the bar-descriptor.xml file, define the orientation of your application on start up and behavior during runtime.
if (BPS_SUCCESS != navigator_rotation_lock(false)) {
    fprintf(stderr, "navigator_rotation_lock failed\n");
    return 0;

Last modified: 2015-03-31

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

comments powered by Disqus