Initialize the application

Before we can display anything on the screen using the BlackBerry Native SDK for Tablet OS, we need to do a bit of initialization. We'll initialize the screen so that we can draw on it, and we'll set up some initial data for the Falling Blocks application.

  1. In the main.c file, in main(), create a screen context. We use this context to initialize EGL to use a window surface. EGL is an interface between standard rendering APIs (such as OpenGL ES) and underlying windowing systems. We also use this context to receive screen events.

    screen_create_context(&screen_cxt, 0);

  2. Initialize the BlackBerry Platform Services (BPS) library. This allows the application to receive and process events. You must perform this step before you call any other BlackBerry Platform Services functions.

    bps_initialize();

  3. Determine the initial orientation angle of the tablet. This information is used to handle accelerometer events properly.

    orientation_direction_t direction;
    orientation_get(&direction, &orientation_angle);

  4. 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 Falling Blocks project. The bbutil.h file is a collection of functions that can help you 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 the application. You can take a look at bbutil_init_egl() to see how you can initialize a native surface. You don't have to use the functions in bbutil.h (you can work with EGL directly in your own applications, if you prefer), but they're provided as helper functions and are ready to use. If the EGL initialization doesn't succeed, we print an error to stderr, clean up the EGL surface by calling bbutil_terminate() (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");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        return 0;
    }

  5. Initialize the application data. We call init_blocks() to perform this initialization. If the initialization doesn't succeed, we clean up and close the application.

    if (EXIT_SUCCESS != init_blocks()) {
        fprintf(stderr, "initialize failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        return 0;
    }

  6. In init_blocks(), do the following:
    1. Specify the vertices of the blocks in the application, as well as the maximum size (in pixels) of the blocks that can appear on the screen. Each pair of values represents a corner, or vertex, of a block in 2-D space. It's important to realize that we don't have to specify the vertices of every block in our application. Instead, we can specify the vertices of a generic block and reuse these vertices over and over again for each block we need to display. This approach improves performance, and is the preferred approach when you must render shapes using OpenGL ES in your applications.

      vertices[0] = 0.0f;
      vertices[1] = 0.0f;
      
      vertices[2] = 1.0f;
      vertices[3] = 0.0f;
      
      vertices[4] = 0.0f;
      vertices[5] = 1.0f;
      
      vertices[6] = 1.0f;
      vertices[7] = 1.0f;
      
      max_size = 60.0;

    2. Obtain the width and height of the EGL surface that was created using the initialization functions in bbutil.h. We'll use these values to properly position the blocks on the screen. We also determine if any errors occurred while obtaining the width and height values.

      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 querry egl surface dimensions\n");
          return EXIT_FAILURE;
      }
      	
      width = (float)surface_width;
      height = (float)surface_height;

    3. Initialize OpenGL ES for 2-D rendering, and set up the OpenGL ES world coordinates to correspond directly with screen pixels on the tablet. This ensures that when we render our scene, the blocks appear in the proper positions on the screen.

      glViewport(0, 0, (int) width, (int) height);
      
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      
      glOrthof(0.0f, width / height, 0.0f, 1.0f, -1.0f, 1.0f);
      
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      
      glScalef(1.0f / height, 1.0f / height, 1.0f);

    4. Set the initial gravity values. We'll use the gravity values later to make sure that the blocks always fall from one side of the screen to the opposite side, regardless of the orientation of the tablet.

      gravity_x = 0.0;
      gravity_y = 0.0;

    5. Allocate memory for the blocks using malloc(), and set the initial number of boxes. Of course, it's important to check the return value on any malloc() calls we use.

      boxes = (box*)malloc(sizeof(box) * MAX_BOXES);
      
      if (!boxes) {
          return EXIT_FAILURE;
      }
      
      num_boxes = 0;

    6. Set the initial background color for our application (here, it's a pleasing shade of green).

      glClearColor(0.0f, 0.25f, 0.0f, 1.0f);

  7. Back in main(), request events from the screen and the navigator so that the application can process them. We want our application to respond to touch events (to add a block to the screen) and navigator events (to clear the screen of blocks and to close the application). We also want the application to respond to accelerometer events (to change the direction that the blocks are falling in), but we'll handle these events a bit later. When we request screen and navigator events, we check to see if 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");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        return 0;
    }
    
    if (BPS_SUCCESS != navigator_request_events(0)) {
        fprintf(stderr, "navigator_request_events failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        return 0;
    }

  8. Indicate to the BlackBerry Platform Services library that the orientation of the application is not to be locked. The library allows you to lock the orientation of your application so that it always operates in either portrait or landscape mode. Because this application is designed to change the position of the blocks when a user rotates the tablet, we don't want to use this locking feature.

    if (BPS_SUCCESS != navigator_rotation_lock(false)) {
        fprintf(stderr, "navigator_rotation_lock failed\n");
        bbutil_terminate();
        screen_destroy_context(screen_cxt);
        return 0;
    	}

  9. Configure the sensor hardware to update the application every 25000 microseconds and to skip duplicate sensor events. We also want our application to respond to sensor events. It's important to note that the BlackBerry Tablet Simulator doesn't support accelerometer functionality currently, so the calculations used in this application are only performed if the application is run on a tablet, otherwise the gravity is set to default values.

    if (sensor_is_supported(SENSOR_TYPE_AZIMUTH_PITCH_ROLL)) {
    
        static const int SENSOR_RATE = 25000;
    
        sensor_set_rate(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, SENSOR_RATE);
    
        sensor_set_skip_duplicates(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, true);
        sensor_request_events(SENSOR_TYPE_AZIMUTH_PITCH_ROLL);
    } else {
        set_gravity(0.0f, -1.0f);
    }

  10. Finally, add a single block to the screen for the user to start with. The add_cube() function adds a block to a specific position on the screen. To make things interesting, the block is a random shade of green and varies in size (within certain size limitations).

    add_cube(200, 100);