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 the documentation on 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.
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:- screen_get_device_property_iv(): This function queries an integer property of a single device.
- screen_get_device_property_cv(): This function retrieves strings associated with a device.
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.