Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1093 lines (924 sloc) 39.005 kB
/*
* 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 <fcntl.h>
#include <bps/bps.h>
#include <bps/event.h>
#include <bps/navigator.h>
#include <bps/screen.h>
#include <png.h>
#include <errno.h>
#include <screen/screen.h>
#include <sys/keycodes.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bbutil.h"
// This macro provides error checking for all calls to libscreen APIs.
static int rc;
#define SCREEN_API(x, y) rc = x; \
if (rc) fprintf(stderr, "\n%s in %s: %d", y, __FUNCTION__, errno)
// Constants used when allocating memory for our graphical data.
static const int QUAD_COUNT = 41;
static const int VERTEX_COORD_COUNT = 328; // QUAD_COUNT * 4 vertices per image * 2 vertex coords per vertex.
static const int INDEX_COUNT = 246; // QUAD_COUNT * 6 indices per quad.
static const int TEXCOORD_COUNT = 328; // QUAD_COUNT * 8 UVs per quad.
// Size and positions of all the controls.
static const float ANALOG0_X = 75.0f;
static const float ANALOG1_X = 460.0f;
static const float ANALOG_Y = 75.0f;
static const float ANALOG_SIZE = 100.0f;
static const float DPAD_X = 25.0f;
static const float DPAD_Y = 275.0f;
static const float DPAD_LONG = 112.5f;
static const float DPAD_SHORT = 75.0f;
static const float BUTTONS_X = 400.0f;
static const float BUTTONS_Y = 275.0f;
static const float BUTTON_SIZE = 75.0f;
static const float LEFT_TRIGGERS_X = 25.0f;
static const float RIGHT_TRIGGERS_X = 450.0f;
static const float TRIGGERS_Y = 525.0f;
static const float TRIGGER_WIDTH = 175.0f;
static const float TRIGGER_HEIGHT = 50.0f;
static const float SELECT_X = 237.5f;
static const float SELECT_Y = 475.0f;
// Other constants.
static const int FONT_SIZE = 4;
// Texture coordinates for each image in our texture atlas.
static GLfloat _outerUVs[4] = { 0.0f, 1.0f, 0.25f, 0.75f };
static GLfloat _innerUVs[4] = { 0.25f, 1.0f, 0.5f, 0.75f };
static GLfloat _buttonDownUVs[4] = { 0.0f, 0.7109375f, 0.166015625f, 0.544921875f };
static GLfloat _buttonUpUVs[4] = { 0.166015625f, 0.7109375f, 0.33203125f, 0.544921875f };
static GLfloat _triggerDownUVs[4] = { 0.0f, 0.51171875f, 0.1474609375f, 0.466796875f };
static GLfloat _triggerUpUVs[4] = { 0.17578125f, 0.51171875f, 0.3232421875f, 0.466796875f };
static GLfloat _upDPadDownUVs[4] = { 0.0f, 0.267578125f, 0.1005859375f, 0.1201171875f };
static GLfloat _upDPadUpUVs[4] = { 0.390625f, 0.267578125f, 0.4912109375f, 0.1201171875f };
static GLfloat _downDPadDownUVs[4] = { 0.17578125f, 0.267578125f, 0.2763671875f, 0.1201171875f };
static GLfloat _downDPadUpUVs[4] = { 0.56640625f, 0.267578125f, 0.6669921875f, 0.1201171875f };
static GLfloat _leftDPadDownUVs[4] = { 0.0f, 0.4140625f, 0.1474609375f, 0.3125f };
static GLfloat _leftDPadUpUVs[4] = { 0.390625f, 0.4140625f, 0.5380859375f, 0.3125f };
static GLfloat _rightDPadDownUVs[4] = { 0.17578125f, 0.4140625f, 0.3232421875f, 0.3125f };
static GLfloat _rightDPadUpUVs[4] = { 0.56640625f, 0.4140625f, 0.7138671875f, 0.3125f } ;
// Each button type corresponds to a set of texture coordinates from those defined above.
typedef enum ButtonType_t {
DPAD_UP,
DPAD_DOWN,
DPAD_LEFT,
DPAD_RIGHT,
DIGITAL_TRIGGER,
ANALOG_TRIGGER,
BUTTON
} ButtonType;
// This structure defines a quad that can be drawn to the screen.
typedef struct Quad_t {
GLfloat x;
GLfloat y;
GLfloat width;
GLfloat height;
GLfloat* uvs;
} Quad;
// This structure contains everything needed to map a physical button on a device to a virtual button on the screen.
typedef struct Button_t {
// A button's type determines which set of UVs it uses.
ButtonType type;
int mapping;
Quad* quad;
char* label;
} Button;
// The possible values for Button.mapping.
static int _buttonMappings[32];
// Objects used by the application.
static screen_context_t _screen_ctx;
static font_t* _font;
// Miscellaneous variables used by the application.
static bool _shutdown;
static bool _polling;
static float _surfaceHeight;
static float _surfaceWidth;
// Storage for our graphical data.
static unsigned int _gamepadTexture;
static GLfloat* _vertices;
static GLushort* _indices;
static GLfloat* _textureCoords;
static Quad _quads[41];
// Pointers to the quads and buttons we'll need to modify during frame updates.
static Quad* _analog0Inner[2];
static Quad* _analog1Inner[2];
static Button _buttons[2][16];
// Tapping an on-screen button will make it 'active', and the next gamepad button-press will map that gamepad button to this on-screen button.
static Button* _activeButton[2];
static Button _pollingButton;
// Structure representing a game controller.
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;
// Controller information.
static int MAX_CONTROLLERS = 2;
static int MAX_BUTTONS = 16;
static GameController _controllers[2];
static void initController(GameController* controller, int player)
{
// Initialize controller values.
controller->handle = 0;
controller->type = 0;
controller->analogCount = 0;
controller->buttonCount = 0;
controller->buttons = 0;
controller->analog0[0] = controller->analog0[1] = controller->analog0[2] = 0;
controller->analog1[0] = controller->analog1[1] = controller->analog1[2] = 0;
sprintf(controller->deviceString, "Player %d: No device detected.", player + 1);
}
int init()
{
// Initialize our static variables and controllers.
_font = NULL;
_shutdown = false;
_polling = false;
int i;
for (i = 0; i < MAX_CONTROLLERS; ++i) {
initController(&_controllers[i], i);
_activeButton[i] = NULL;
}
// Populate an array of button mappings.
for (i = 0; i < 32; ++i) {
_buttonMappings[i] = 1 << i;
}
EGLint surface_width, surface_height;
// Query width and height of the window surface created by utility code.
eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width);
eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height);
EGLint err = eglGetError();
if (err != EGL_SUCCESS) {
fprintf(stderr, "Unable to query EGL surface dimensions\n");
return EXIT_FAILURE;
}
_surfaceHeight = (float) surface_height;
_surfaceWidth = (float) surface_width;
// Calculate our display's DPI and load our font using utility code.
int dpi = bbutil_calculate_dpi(_screen_ctx);
_font = bbutil_load_font("/usr/fonts/font_repository/monotype/cour.ttf", FONT_SIZE, dpi);
if (!_font) {
fprintf(stderr, "Unable to load font.\n");
return EXIT_FAILURE;
}
// Load our gamepad texture atlas.
int w, h;
float tx, ty;
if (bbutil_load_texture("./app/native/gamepad.png", &w, &h, &tx, &ty, &_gamepadTexture) == EXIT_FAILURE) {
fprintf(stderr, "Unable to load texture.\n");
return EXIT_FAILURE;
}
// Set the initial positions of all joysticks and buttons.
for (i = 0; i < MAX_CONTROLLERS; ++i) {
/**
* Quads | Buttons | Description
* =======|==========|=============
* 0, 1 | 0, 1 | Analog triggers.
* 2-7 | 2, 3 | Analog sticks and their buttons.
* 6-9 | 4 - 7 | D-Pad. Up, Down, Left, Right.
* 10-13 | 8 - 12 | A, B, X, Y Buttons.
* 14-17 | 13, 14 | Triggers: L1, R1
* 18, 19 | 15, 16 | Select, Start.
*/
int controllerIndex = 20*i;
float xOffset = (_surfaceWidth * 0.5f)*i;
int j;
// Assign quads to all buttons other than L2, R2, L3 and R3.
for (j = 4; j < 16; ++j) {
_buttons[i][j].quad = &_quads[j+4 + controllerIndex];
}
// D-Pad.
for (j = 4; j < 8; ++j) {
_buttons[i][j].type = DPAD_UP + j-4;
}
// Buttons.
for (j = 8; j < 12; ++j) {
_buttons[i][j].type = BUTTON;
_buttons[i][j].quad->width = BUTTON_SIZE;
_buttons[i][j].quad->height = BUTTON_SIZE;
_buttons[i][j].quad->uvs = _buttonUpUVs;
}
// Triggers.
for (j = 12; j < 16; ++j) {
_buttons[i][j].type = DIGITAL_TRIGGER;
_buttons[i][j].quad->width = TRIGGER_WIDTH;
_buttons[i][j].quad->height = TRIGGER_HEIGHT;
_buttons[i][j].quad->uvs = _triggerUpUVs;
}
// Set quad positions and sizes.
// Analog triggers
_buttons[i][0].label = "L2";
_buttons[i][0].type = ANALOG_TRIGGER;
_buttons[i][0].quad = &_quads[0 + controllerIndex];
_buttons[i][0].quad->x = LEFT_TRIGGERS_X + xOffset;
_buttons[i][0].quad->y = TRIGGERS_Y + TRIGGER_HEIGHT + 25.0f;
_buttons[i][0].quad->width = TRIGGER_WIDTH;
_buttons[i][0].quad->height = TRIGGER_HEIGHT;
_buttons[i][0].quad->uvs = _triggerDownUVs;
_buttons[i][0].mapping = SCREEN_L2_GAME_BUTTON;
_buttons[i][1].label = "R2";
_buttons[i][1].type = ANALOG_TRIGGER;
_buttons[i][1].quad = &_quads[1 + controllerIndex];
_buttons[i][1].quad->x = RIGHT_TRIGGERS_X + xOffset;
_buttons[i][1].quad->y = TRIGGERS_Y + TRIGGER_HEIGHT + 25.0f;
_buttons[i][1].quad->width = TRIGGER_WIDTH;
_buttons[i][1].quad->height = TRIGGER_HEIGHT;
_buttons[i][1].quad->uvs = _triggerDownUVs;
_buttons[i][1].mapping = SCREEN_R2_GAME_BUTTON;
// Right stick
Quad* analog1Outer = &_quads[2 + controllerIndex];
analog1Outer->x = ANALOG1_X + xOffset;
analog1Outer->y = ANALOG_Y;
analog1Outer->width = analog1Outer->height = ANALOG_SIZE;
analog1Outer->uvs = _outerUVs;
_analog1Inner[i] = &_quads[3 + controllerIndex];
_analog1Inner[i]->x = ANALOG1_X + xOffset;
_analog1Inner[i]->y = ANALOG_Y;
_analog1Inner[i]->width = _analog1Inner[i]->height = ANALOG_SIZE;
_analog1Inner[i]->uvs = _innerUVs;
// R3
_buttons[i][2].label = "R3";
_buttons[i][2].type = BUTTON;
_buttons[i][2].quad = &_quads[4 + controllerIndex];
_buttons[i][2].quad->x = ANALOG1_X - BUTTON_SIZE*2.0f + xOffset;
_buttons[i][2].quad->y = ANALOG_Y + BUTTON_SIZE;
_buttons[i][2].quad->width = BUTTON_SIZE;
_buttons[i][2].quad->height = BUTTON_SIZE;
_buttons[i][2].quad->uvs = _buttonUpUVs;
_buttons[i][2].mapping = SCREEN_R3_GAME_BUTTON;
// Left stick
Quad* analog0Outer = &_quads[5 + controllerIndex];
analog0Outer->x = ANALOG0_X + xOffset;
analog0Outer->y = ANALOG_Y;
analog0Outer->width = analog0Outer->height = ANALOG_SIZE;
analog0Outer->uvs = _outerUVs;
_analog0Inner[i] = &_quads[6 + controllerIndex];
_analog0Inner[i]->x = ANALOG0_X + xOffset;
_analog0Inner[i]->y = ANALOG_Y;
_analog0Inner[i]->width = _analog0Inner[i]->height = ANALOG_SIZE;
_analog0Inner[i]->uvs = _innerUVs;
// L3
_buttons[i][3].quad = &_quads[7 + controllerIndex];
_buttons[i][3].type = BUTTON;
_buttons[i][3].label = "L3";
_buttons[i][3].quad->x = ANALOG0_X + BUTTON_SIZE*2.0f + xOffset;
_buttons[i][3].quad->y = ANALOG_Y + BUTTON_SIZE;
_buttons[i][3].quad->width = BUTTON_SIZE;
_buttons[i][3].quad->height = BUTTON_SIZE;
_buttons[i][3].quad->uvs = _buttonUpUVs;
_buttons[i][3].mapping = SCREEN_L3_GAME_BUTTON;
// Up
_buttons[i][4].label = "U";
_buttons[i][4].quad->x = DPAD_X + DPAD_SHORT + xOffset;
_buttons[i][4].quad->y = DPAD_Y + DPAD_LONG;
_buttons[i][4].quad->width = DPAD_SHORT;
_buttons[i][4].quad->height = DPAD_LONG;
_buttons[i][4].quad->uvs = _upDPadUpUVs;
_buttons[i][4].mapping = SCREEN_DPAD_UP_GAME_BUTTON;
// Down
_buttons[i][5].label = "D";
_buttons[i][5].quad->x = DPAD_X + DPAD_SHORT + xOffset;
_buttons[i][5].quad->y = DPAD_Y;
_buttons[i][5].quad->width = DPAD_SHORT;
_buttons[i][5].quad->height = DPAD_LONG;
_buttons[i][5].quad->uvs = _downDPadUpUVs;
_buttons[i][5].mapping = SCREEN_DPAD_DOWN_GAME_BUTTON;
// Left
_buttons[i][6].label = "L";
_buttons[i][6].quad->x = DPAD_X + xOffset;
_buttons[i][6].quad->y = DPAD_Y + DPAD_SHORT;
_buttons[i][6].quad->width = DPAD_LONG;
_buttons[i][6].quad->height = DPAD_SHORT;
_buttons[i][6].quad->uvs = _leftDPadUpUVs;
_buttons[i][6].mapping = SCREEN_DPAD_LEFT_GAME_BUTTON;
// Right
_buttons[i][7].label = "R";
_buttons[i][7].quad->x = DPAD_X + DPAD_LONG + xOffset;
_buttons[i][7].quad->y = DPAD_Y + DPAD_SHORT;
_buttons[i][7].quad->width = DPAD_LONG;
_buttons[i][7].quad->height = DPAD_SHORT;
_buttons[i][7].quad->uvs = _rightDPadUpUVs;
_buttons[i][7].mapping = SCREEN_DPAD_RIGHT_GAME_BUTTON;
// A, B, X, Y
_buttons[i][8].label = "A";
_buttons[i][8].quad->x = BUTTONS_X + BUTTON_SIZE + xOffset;
_buttons[i][8].quad->y = BUTTONS_Y;
_buttons[i][8].mapping = SCREEN_A_GAME_BUTTON;
_buttons[i][9].label = "B";
_buttons[i][9].quad->x = BUTTONS_X + 2*BUTTON_SIZE + xOffset;
_buttons[i][9].quad->y = BUTTONS_Y + BUTTON_SIZE;
_buttons[i][9].mapping = SCREEN_B_GAME_BUTTON;
_buttons[i][10].label = "X";
_buttons[i][10].quad->x = BUTTONS_X + xOffset;
_buttons[i][10].quad->y = BUTTONS_Y + BUTTON_SIZE;
_buttons[i][10].mapping = SCREEN_X_GAME_BUTTON;
_buttons[i][11].label = "Y";
_buttons[i][11].quad->x = BUTTONS_X + BUTTON_SIZE + xOffset;
_buttons[i][11].quad->y = BUTTONS_Y + 2*BUTTON_SIZE;
_buttons[i][11].mapping = SCREEN_Y_GAME_BUTTON;
// L1, R1
_buttons[i][12].label = "L1";
_buttons[i][12].quad->x = LEFT_TRIGGERS_X + xOffset;
_buttons[i][12].quad->y = TRIGGERS_Y;
_buttons[i][12].mapping = SCREEN_L1_GAME_BUTTON;
_buttons[i][13].label = "R1";
_buttons[i][13].quad->x = RIGHT_TRIGGERS_X + xOffset;
_buttons[i][13].quad->y = TRIGGERS_Y;
_buttons[i][13].mapping = SCREEN_R1_GAME_BUTTON;
// Select, Start
_buttons[i][14].label = "Select";
_buttons[i][14].quad->x = SELECT_X + xOffset;
_buttons[i][14].quad->y = SELECT_Y;
_buttons[i][14].mapping = SCREEN_MENU1_GAME_BUTTON;
_buttons[i][15].label = "Start";
_buttons[i][15].quad->x = SELECT_X + xOffset;
_buttons[i][15].quad->y = SELECT_Y + TRIGGER_HEIGHT + 25.0f;
_buttons[i][15].mapping = SCREEN_MENU2_GAME_BUTTON;
}
// Finally, one last quad is used for the "polling" button.
Quad* pollingQuad = &_quads[40];
pollingQuad->x = (surface_width * 0.5f) - TRIGGER_WIDTH * 0.5f;
pollingQuad->y = 5.0f;
pollingQuad->width = TRIGGER_WIDTH;
pollingQuad->height = TRIGGER_HEIGHT + 20;
pollingQuad->uvs = _triggerUpUVs;
_pollingButton.quad = pollingQuad;
_pollingButton.type = DIGITAL_TRIGGER;
_pollingButton.label = "Polling";
// Create our vertex and texture coordinate arrays.
_vertices = (GLfloat*) calloc(VERTEX_COORD_COUNT, sizeof(GLfloat));
_textureCoords = (GLfloat*) calloc(TEXCOORD_COUNT, sizeof(GLfloat));
// Create and initialize our index array.
_indices = (GLushort*) calloc(INDEX_COUNT, sizeof(GLushort));
_indices[0] = 0;
_indices[1] = 1;
_indices[2] = 2;
_indices[3] = 3;
_indices[4] = 3;
_indices[5] = 4;
int vertexCount = 4;
for (i = 1; i < QUAD_COUNT; ++i) {
_indices[i*6] = vertexCount;
_indices[i*6 + 1] = 1 + vertexCount;
_indices[i*6 + 2] = 2 + vertexCount;
_indices[i*6 + 3] = 3 + vertexCount;
_indices[i*6 + 4] = 3 + vertexCount;
vertexCount += 4;
_indices[i*6 + 5] = vertexCount;
}
// Initialize OpenGL for 2D rendering.
glViewport(0, 0, surface_width, surface_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Set world coordinates to coincide with screen pixels.
glScalef(1.0f / (float)surface_width, 1.0f / _surfaceHeight, 1.0f);
return EXIT_SUCCESS;
}
void finalize()
{
// Free allocated resources.
if (_vertices) {
free(_vertices);
_vertices = NULL;
}
if (_indices) {
free(_indices);
_indices = NULL;
}
if (_textureCoords) {
free(_textureCoords);
_textureCoords = NULL;
}
// Destroy the font.
bbutil_destroy_font(_font);
// Stop requesting events from libscreen.
SCREEN_API(screen_stop_events(_screen_ctx), "stop_events");
// Shut down BPS library for this process.
bps_shutdown();
// Use utility code to terminate EGL setup.
bbutil_terminate();
// Destroy libscreen context.
SCREEN_API(screen_destroy_context(_screen_ctx), "destroy_context");
}
static void loadController(GameController* controller)
{
// 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->analog1)) {
++controller->analogCount;
}
if (controller->type == SCREEN_EVENT_GAMEPAD) {
sprintf(controller->deviceString, "Gamepad device ID: %s", controller->id);
} else {
sprintf(controller->deviceString, "Joystick device: %s", controller->id);
}
}
static void pollDevices()
{
int i;
for (i = 0; i < MAX_CONTROLLERS; i++) {
GameController* controller = &_controllers[i];
if (controller->handle) {
// Get the current state of a gamepad device.
SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_BUTTONS, &controller->buttons), "SCREEN_PROPERTY_BUTTONS");
if (controller->analogCount > 0) {
SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_ANALOG0, controller->analog0), "SCREEN_PROPERTY_ANALOG0");
}
if (controller->analogCount == 2) {
SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_ANALOG1, controller->analog1), "SCREEN_PROPERTY_ANALOG1");
}
}
}
}
static void handleScreenEvent(bps_event_t *event)
{
int eventType;
screen_event_t screen_event = screen_event_get_event(event);
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType);
switch (eventType) {
case SCREEN_EVENT_GAMEPAD:
case SCREEN_EVENT_JOYSTICK:
{
if (!_polling) {
// Determine which controller this is.
screen_device_t device;
SCREEN_API(screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_DEVICE, (void**)&device), "SCREEN_PROPERTY_DEVICE");
GameController* controller = NULL;
int i;
for (i = 0; i < MAX_CONTROLLERS; ++i) {
if (device == _controllers[i].handle) {
controller = &_controllers[i];
break;
}
}
if (!controller) {
break;
}
// Store the controller's new state.
SCREEN_API(screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_BUTTONS, &controller->buttons), "SCREEN_PROPERTY_BUTTONS");
if (controller->analogCount > 0) {
SCREEN_API(screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_ANALOG0, controller->analog0), "SCREEN_PROPERTY_ANALOG0");
}
if (controller->analogCount == 2) {
SCREEN_API(screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_ANALOG1, controller->analog1), "SCREEN_PROPERTY_ANALOG1");
}
}
break;
}
case SCREEN_EVENT_DEVICE:
{
// A device was attached or removed.
screen_device_t device;
int attached;
int type;
SCREEN_API(screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_DEVICE, (void**)&device), "SCREEN_PROPERTY_DEVICE");
SCREEN_API(screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_ATTACHED, &attached), "SCREEN_PROPERTY_ATTACHED");
if (attached) {
SCREEN_API(screen_get_device_property_iv(device, SCREEN_PROPERTY_TYPE, &type), "SCREEN_PROPERTY_TYPE");
}
int i;
if (attached && (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)) {
for (i = 0; i < MAX_CONTROLLERS; ++i) {
if (!_controllers[i].handle) {
_controllers[i].handle = device;
loadController(&_controllers[i]);
break;
}
}
} else {
for (i = 0; i < MAX_CONTROLLERS; ++i) {
if (device == _controllers[i].handle) {
initController(&_controllers[i], i);
break;
}
}
}
break;
}
case SCREEN_EVENT_MTOUCH_RELEASE:
{
int pos[2];
SCREEN_API(screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION, pos), "SCREEN_PROPERTY_SOURCE_POSITION");
int x = pos[0];
int y = _surfaceHeight - pos[1];
int i;
for (i = 0; i < MAX_CONTROLLERS; ++i) {
bool buttonTapped = false;
int j;
for (j = 0; j < MAX_BUTTONS; ++j) {
Button* button = &_buttons[i][j];
Quad* quad = button->quad;
// Detect that a button was tapped.
if (x > quad->x && x < quad->x + quad->width &&
y > quad->y && y < quad->y + quad->height) {
_activeButton[i] = button;
buttonTapped = true;
break;
}
}
if (_activeButton[i] && !buttonTapped) {
// Cancel the button's active state.
_activeButton[i] = NULL;
}
}
// The polling button is used to switch between handling all device events and polling devices once per frame.
Quad* quad = _pollingButton.quad;
if (x > quad->x && x < quad->x + quad->width &&
y > quad->y && y < quad->y + quad->height) {
_polling = !_polling;
// The polling button lights up when polling is enabled.
if (_polling) {
quad->uvs = _triggerDownUVs;
} else {
quad->uvs = _triggerUpUVs;
}
}
break;
}
}
}
static void handleNavigatorEvent(bps_event_t *event)
{
switch (bps_event_get_code(event)) {
case NAVIGATOR_EXIT:
_shutdown = true;
break;
default:
break;
}
}
static void handleEvents()
{
int domain;
// Get the first event in the queue.
bps_event_t *event = NULL;
if (BPS_SUCCESS != bps_get_event(&event, 0)) {
fprintf(stderr, "bps_get_event() failed\n");
return;
}
// Handle all events in the queue.
// If we don't do this in a loop, we'll only handle one event per frame.
// If many events are triggered quickly, e.g. by spinning the analog sticks,
// the queue will grow and the user will see the analog sticks lag.
while (event) {
if (event) {
domain = bps_event_get_domain(event);
if (domain == navigator_get_domain()) {
handleNavigatorEvent(event);
} else if (domain == screen_get_domain()) {
handleScreenEvent(event);
}
}
if (BPS_SUCCESS != bps_get_event(&event, 0)) {
fprintf(stderr, "bps_get_event() failed\n");
return;
}
}
}
static void discoverControllers()
{
// Get an array of all available devices.
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");
// Scan the list for gamepad and joystick devices.
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;
}
}
}
free(devices);
}
void update()
{
handleEvents();
if (_polling) {
pollDevices();
}
int i;
for (i = 0; i < MAX_CONTROLLERS; ++i) {
GameController* controller = &_controllers[i];
// If a button is active, map it to the first gamepad button pressed.
if (_activeButton[i]) {
int j;
for (j = 0; j < controller->buttonCount; ++j) {
if (controller->buttons & _buttonMappings[j]) {
// Erase old button mapping if one exists.
int k;
for (k = 0; k < MAX_BUTTONS; ++k) {
Button* button = &_buttons[i][k];
if (_buttonMappings[j] & button->mapping) {
button->mapping = 0;
break;
}
}
// Set new button mapping.
_activeButton[i]->mapping = _buttonMappings[j];
_activeButton[i] = NULL;
break;
}
}
}
float xOffset = (_surfaceWidth * 0.5f)*i;
// Set the inner joystick positions.
if (controller->analogCount > 0) {
_analog0Inner[i]->x = ANALOG0_X + xOffset + (controller->analog0[0] >> 2);
_analog0Inner[i]->y = ANALOG_Y - (controller->analog0[1] >> 2);
sprintf(controller->analog0String, "Analog 0: (%4d, %4d, %4d)", controller->analog0[0], controller->analog0[1], controller->analog0[2]);
} else {
sprintf(controller->analog0String, "Analog 0: N/A");
}
if (controller->analogCount == 2) {
_analog1Inner[i]->x = ANALOG1_X + xOffset + (controller->analog1[0] >> 2);
_analog1Inner[i]->y = ANALOG_Y - (controller->analog1[1] >> 2);
sprintf(controller->analog1String, "Analog 1: (%4d, %4d, %4d)", controller->analog1[0], controller->analog1[1], controller->analog1[2]);
} else {
sprintf(controller->analog1String, "Analog 1: N/A");
}
// Set the button UVs to correspond to their states, as well as the button text.
sprintf(controller->buttonsString, "Buttons: ");
int j;
for (j = 0; j < MAX_BUTTONS; ++j) {
Button* button = &_buttons[i][j];
if ((controller->buttons & button->mapping) || button == _activeButton[i]) {
switch (button->type) {
case DPAD_LEFT:
button->quad->uvs = _leftDPadDownUVs;
break;
case DPAD_RIGHT:
button->quad->uvs = _rightDPadDownUVs;
break;
case DPAD_UP:
button->quad->uvs = _upDPadDownUVs;
break;
case DPAD_DOWN:
button->quad->uvs = _downDPadDownUVs;
break;
case DIGITAL_TRIGGER:
case ANALOG_TRIGGER:
button->quad->uvs = _triggerDownUVs;
break;
case BUTTON:
button->quad->uvs = _buttonDownUVs;
break;
}
} else {
switch (button->type) {
case DPAD_LEFT:
button->quad->uvs = _leftDPadUpUVs;
break;
case DPAD_RIGHT:
button->quad->uvs = _rightDPadUpUVs;
break;
case DPAD_UP:
button->quad->uvs = _upDPadUpUVs;
break;
case DPAD_DOWN:
button->quad->uvs = _downDPadUpUVs;
break;
case DIGITAL_TRIGGER:
case ANALOG_TRIGGER:
button->quad->uvs = _triggerUpUVs;
break;
case BUTTON:
button->quad->uvs = _buttonUpUVs;
break;
}
}
}
// Append button indices to the string showing which buttons are pressed,
// regardless of whether or not the buttons are mapped.
char buttonString[4];
for (j = 0; j < controller->buttonCount; ++j) {
if (_buttonMappings[j] & controller->buttons) {
sprintf(buttonString, "%d ", j);
strcat(controller->buttonsString, buttonString);
}
}
}
}
void render()
{
// Clear the screen.
glClear(GL_COLOR_BUFFER_BIT);
// Populate vertex and texture coordinate arrays.
int i;
for (i = 0; i < QUAD_COUNT; ++i) {
const GLfloat x = _quads[i].x;
const GLfloat y = _quads[i].y;
const GLfloat width = _quads[i].width;
const GLfloat height = _quads[i].height;
_vertices[i*8 + 0] = x;
_vertices[i*8 + 1] = y;
_vertices[i*8 + 2] = x + width;
_vertices[i*8 + 3] = y;
_vertices[i*8 + 4] = x;
_vertices[i*8 + 5] = y + height;
_vertices[i*8 + 6] = x + width;
_vertices[i*8 + 7] = y + height;
const GLfloat u1 = _quads[i].uvs[0];
const GLfloat v1 = _quads[i].uvs[1];
const GLfloat u2 = _quads[i].uvs[2];
const GLfloat v2 = _quads[i].uvs[3];
_textureCoords[i*8 + 0] = u1;
_textureCoords[i*8 + 1] = v2;
_textureCoords[i*8 + 2] = u2;
_textureCoords[i*8 + 3] = v2;
_textureCoords[i*8 + 4] = u1;
_textureCoords[i*8 + 5] = v1;
_textureCoords[i*8 + 6] = u2;
_textureCoords[i*8 + 7] = v1;
}
// Draw the virtual gamepad.
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, _vertices);
glTexCoordPointer(2, GL_FLOAT, 0, _textureCoords);
glBindTexture(GL_TEXTURE_2D, _gamepadTexture);
if (_controllers[0].handle || _controllers[1].handle) {
// Draw the polling button.
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, _indices + 240);
}
// Draw only connected controllers.
// L2 and R2 require special attention.
// On many controllers, L2 and R2 are analog triggers instead of digital buttons.
// First we check to see if the button is down. If so, we treat the triggers as being "all the way down".
// Otherwise, we tint L2 and R2 using the analog values from the triggers.
// Only draw the analog sticks and their buttons (L3, R3) if they're present.
for (i = 0; i < MAX_CONTROLLERS; ++i) {
GameController* controller = &_controllers[i];
if (controller->handle) {
float tint = 1.0f;
if (!(controller->buttons & _buttons[i][0].mapping) && &_buttons[i][0] != _activeButton[i]) {
tint = 0.5f + 0.5f*(float)controller->analog0[2] / 255.0f;
}
glColor4f(tint, 0.0f, 0.0f, 1.0f);
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, _indices + i*120);
tint = 1.0f;
if (!(controller->buttons & _buttons[i][1].mapping) && &_buttons[i][1] != _activeButton[i]) {
tint = 0.5f + 0.5f*(float)controller->analog1[2] / 255.0f;
}
glColor4f(tint, 0.0f, 0.0f, 1.0f);
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, _indices + 6 + i*120);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
if (controller->analogCount == 2) {
glDrawElements(GL_TRIANGLE_STRIP, 108, GL_UNSIGNED_SHORT, _indices + 12 + i*120);
} else if (controller->analogCount == 1) {
glDrawElements(GL_TRIANGLE_STRIP, 90, GL_UNSIGNED_SHORT, _indices + 30 + i*120);
} else {
glDrawElements(GL_TRIANGLE_STRIP, 72, GL_UNSIGNED_SHORT, _indices + 48 + i*120);
}
}
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
// Use utility code to render text.
// Only draw L3 and R3 labels if they're present.
for (i = 0; i < MAX_CONTROLLERS; ++i) {
GameController* controller = &_controllers[i];
if (controller->handle) {
if (controller->analogCount == 2) {
bbutil_render_text(_font, _buttons[i][2].label, _buttons[i][2].quad->x + 30, _buttons[i][2].quad->y + 30, 1.0f, 0.0f, 0.0f, 1.0f);
bbutil_render_text(_font, _buttons[i][3].label, _buttons[i][3].quad->x + 30, _buttons[i][3].quad->y + 30, 1.0f, 0.0f, 0.0f, 1.0f);
} else if (controller->analogCount == 1) {
bbutil_render_text(_font, _buttons[i][3].label, _buttons[i][3].quad->x + 30, _buttons[i][3].quad->y + 30, 1.0f, 0.0f, 0.0f, 1.0f);
}
}
}
// Now render the rest of the text.
for (i = 0; i < MAX_CONTROLLERS; ++i) {
GameController* controller = &_controllers[i];
float xOffset = (_surfaceWidth * 0.5f)*i;
bbutil_render_text(_font, controller->deviceString, 5 + xOffset, _surfaceHeight - 20, 1.0f, 0.0f, 0.0f, 1.0f);
if (controller->handle) {
// Controller is connected; display info about its current state.
bbutil_render_text(_font, controller->buttonsString, 5 + xOffset, _surfaceHeight - 40, 1.0f, 0.0f, 0.0f, 1.0f);
bbutil_render_text(_font, controller->analog0String, 5 + xOffset, _surfaceHeight - 60, 1.0f, 0.0f, 0.0f, 1.0f);
bbutil_render_text(_font, controller->analog1String, 5 + xOffset, _surfaceHeight - 80, 1.0f, 0.0f, 0.0f, 1.0f);
// L2, R2 labels.
bbutil_render_text(_font, _buttons[i][0].label, _buttons[i][0].quad->x + 20, _buttons[i][0].quad->y + 20, 1.0f, 0.0f, 0.0f, 1.0f);
bbutil_render_text(_font, _buttons[i][1].label, _buttons[i][1].quad->x + 20, _buttons[i][1].quad->y + 20, 1.0f, 0.0f, 0.0f, 1.0f);
// Button labels.
int j;
for (j = 4; j < MAX_BUTTONS; ++j) {
Button* button = &_buttons[i][j];
if (button->type == DIGITAL_TRIGGER) {
bbutil_render_text(_font, button->label, button->quad->x + 20, button->quad->y + 20, 1.0f, 0.0f, 0.0f, 1.0f);
} else if (button->type == DPAD_UP) {
bbutil_render_text(_font, button->label, button->quad->x + 30, button->quad->y + 70, 1.0f, 0.0f, 0.0f, 1.0f);
} else if (button->type == DPAD_RIGHT) {
bbutil_render_text(_font, button->label, button->quad->x + 70, button->quad->y + 30, 1.0f, 0.0f, 0.0f, 1.0f);
} else {
bbutil_render_text(_font, button->label, button->quad->x + 30, button->quad->y + 30, 1.0f, 0.0f, 0.0f, 1.0f);
}
}
}
}
if (_controllers[0].handle || _controllers[1].handle) {
bbutil_render_text(_font, _pollingButton.label, _pollingButton.quad->x + 20, _pollingButton.quad->y + 20, 1.0f, 0.0f, 0.0f, 1.0f);
}
// Use utility code to update the screen.
bbutil_swap();
}
int main(int argc, char **argv)
{
// Create a screen context that will be used to create an EGL surface to receive libscreen events.
SCREEN_API(screen_create_context(&_screen_ctx, SCREEN_APPLICATION_CONTEXT), "create_context");
// Initialize BPS library.
bps_initialize();
// Use utility code to initialize EGL for rendering with GL ES 1.1.
if (EXIT_SUCCESS != bbutil_init_egl(_screen_ctx)) {
fprintf(stderr, "Unable to initialize EGL.\n");
screen_destroy_context(_screen_ctx);
return 0;
}
// Initialize app data.
if (EXIT_SUCCESS != init()) {
fprintf(stderr, "Unable to initialize app logic.\n");
bbutil_terminate();
screen_destroy_context(_screen_ctx);
return 0;
}
// Signal BPS library that navigator and screen events will be requested.
if (BPS_SUCCESS != screen_request_events(_screen_ctx)) {
fprintf(stderr, "screen_request_events failed\n");
bbutil_terminate();
screen_destroy_context(_screen_ctx);
return 0;
}
if (BPS_SUCCESS != navigator_request_events(0)) {
fprintf(stderr, "navigator_request_events failed\n");
bbutil_terminate();
screen_destroy_context(_screen_ctx);
return 0;
}
// Look for attached gamepad and joystick devices.
discoverControllers();
// Enter the event loop.
while (!_shutdown) {
update();
render();
}
// Clean up resources and shut everything down.
finalize();
return 0;
}
Jump to Line
Something went wrong with that request. Please try again.