Set up the application

The first thing that runs when the Gamepad application starts is the main() function in main.c. This function creates a screen context, initializes EGL and OpenGL ES, and tells the BlackBerry Platform Services library that the app handles navigator and screen events. It also contains the event loop used to update and render the display.

This sample wraps calls to the Screen and Windowing Library in a macro, SCREEN_API, that prints errors to the console. Use of this macro is optional but it can help you find bugs and notify you if something goes wrong. This macro is defined at the beginning of main.c:
static int rc;
#define SCREEN_API(x, y) rc = x; \
if (rc) fprintf(stderr, "\n%s in %s: %d", y, __FUNCTION__, errno)
Note that certain function calls in the Screen and Windowing Library are queued on a command buffer before executing, resulting in return codes that indicate the status of all commands executed. This means that the output of this macro may reflect the status of more functions than the one just called. For more information, see Function execution types.
There are also a few structs to keep the code organized. The most important of these is the GameController struct, which represents a single attached gamepad or joystick.
typedef struct GameController_t {
    // Static device info.
    screen_device_t handle;
    int type;
    int analogCount;
    int buttonCount;
    char id[64];

    // Current state.
    int buttons;
    int analog0[3];
    int analog1[3];

    // Text to display to the user about this controller.
    char deviceString[256];
    char buttonsString[128];
    char analog0String[128];
    char analog1String[128];
} GameController;
The first group of struct members contains information about the controller that will not change once the controller is connected. This information is assigned in the loadController() function when a new device is discovered or connected. The second group of struct members stores the controller's current state at any given moment. The third group is a collection of strings we update at runtime and render to the display using functions from bbutil.c. The GameController struct is collected into an array called _controllers[] to represent all devices connected to the application.

This app uses a simple text display and representations of the controller's buttons, joystick, and D-pad on the screen that react to input events. The init() function in main.c is called after EGL and OpenGL ES are initialized. This function initializes the values in our _controllers[] array by calling initController(), then loads a font and the virtual gamepad's texture atlas. After this, the init() function initializes all the data used for rendering and for keeping track of the mapping between physical gamepad buttons and virtual buttons on the screen. The logic of the code here isn't specific to the API functions in the Screen and Windowing Library used for gamepad, it just happens to be the way that information is displayed to the user.

Discover game controllers

Before the Gamepad application enters its event loop, the discoverControllers() function is called to determine whether any game controllers are already connected. The following functions from the Screen and Windowing Library are used to get this information:
  • screen_get_context_property_iv(): When called with the property SCREEN_PROPERTY_DEVICE_COUNT, this function reports the number of devices currently attached to the BlackBerry 10 device.
  • screen_get_context_property_pv(): When called with the property SCREEN_PROPERTY_DEVICES, this function populates an array with handles of type screen_device_t, each of which represents a single connected device.
In between calls to these functions, memory is allocated to store the appropriate number of device handles. Putting all this together looks like this:
int deviceCount;
SCREEN_API(screen_get_context_property_iv(_screen_ctx, 
        SCREEN_PROPERTY_DEVICE_COUNT, &deviceCount), 
    "SCREEN_PROPERTY_DEVICE_COUNT");

screen_device_t* devices = (screen_device_t*)calloc(deviceCount,
    sizeof(screen_device_t));

SCREEN_API(screen_get_context_property_pv(_screen_ctx, 
        SCREEN_PROPERTY_DEVICES, (void**)devices), 
    "SCREEN_PROPERTY_DEVICES");
Next, we loop through the array of devices and use the following functions to learn more about each device: Since we're only interested in gamepad and joystick devices, we query each connected device's SCREEN_PROPERTY_TYPE using screen_get_device_property_iv(). If the type is SCREEN_EVENT_GAMEPAD or SCREEN_EVENT_JOYSTICK, the device is assigned to a GameController struct in our _controllers[] array and loadController() is called to query device information, total button count, and the number of analog sticks the controller has. This is how it looks:
int i;
int controllerIndex = 0;
for (i = 0; i < deviceCount; i++) {
    int type;
    SCREEN_API(screen_get_device_property_iv(devices[i], 
        SCREEN_PROPERTY_TYPE, &type), "SCREEN_PROPERTY_TYPE");

    if (!rc && (type == SCREEN_EVENT_GAMEPAD || 
            type == SCREEN_EVENT_JOYSTICK)) {
        // Assign this device to control Player 1 or Player 2.
        GameController* controller = &_controllers[controllerIndex];
        controller->handle = devices[i];
        loadController(controller);

        // We'll just use the first compatible devices we find.
        controllerIndex++;
        if (controllerIndex == MAX_CONTROLLERS) {
            break;
        }
    }
}

In loadController(), we use screen_get_device_property_cv() to query each game controller for its SCREEN_PROPERTY_ID_STRING. This string is formatted as A-BBBB-CCCC-D.D, where A is the device's index in the array returned by screen_get_context_property_pv(), BBBB is the device's Vendor ID (in hexadecimal), CCCC is the device's Product ID (also in hexadecimal), and D.D is the device's version number. This string can be used to determine the exact make and model of a connected device.

Here's what we end up doing in loadController():
// Query libscreen for information about this device.
SCREEN_API(screen_get_device_property_iv(controller->handle, 
        SCREEN_PROPERTY_TYPE, &controller->type), 
    "SCREEN_PROPERTY_TYPE");

SCREEN_API(screen_get_device_property_cv(controller->handle, 
        SCREEN_PROPERTY_ID_STRING, sizeof(controller->id),
        controller->id), 
    "SCREEN_PROPERTY_ID_STRING");

SCREEN_API(screen_get_device_property_iv(controller->handle, 
        SCREEN_PROPERTY_BUTTON_COUNT, &controller->buttonCount), 
    "SCREEN_PROPERTY_BUTTON_COUNT");

// Check for the existence of analog sticks.
if (!screen_get_device_property_iv(controller->handle,
    SCREEN_PROPERTY_ANALOG0, controller->analog0)) {
	++controller->analogCount;
}

if (!screen_get_device_property_iv(controller->handle,
    SCREEN_PROPERTY_ANALOG1, controller->analog0)) {
	++controller->analogCount;
}
The screen_get_device_property_iv() function returns 0 if it succeeds. If it fails for SCREEN_PROPERTY_ANALOG0 or SCREEN_PROPERTY_ANALOG1, we know that this device doesn't have the specified analog stick.

Last modified: 2013-12-24

comments powered by Disqus