Using the keyboard

The event handling framework for BlackBerry 10 is BlackBerry Platform Services (BPS). This framework allows you to register for and receive events from the underlying OS. This includes useful things such as the virtual keyboard, screen, and device sensors. We'll use the BPS library to set up the virtual keyboard.

To use any virtual keyboard function, we'll need to include the appropriate header file at the beginning of main.c:
#include <bps/virtualkeyboard.h>
This application draws a colored square on the screen and uses OpenGL ES to update and render the graphics. The Glview library provided with the BlackBerry 10 Native SDK makes it easy to develop apps with OpenGL ES. It provides an execution loop as well as API functions for an application to register callbacks at different points of execution. We'll use Glview to set up and run the application. To do this, we include the glview header file:
#include <glview/glview.h>
In main(), all we need to do is register three callback functions with Glview and then call glview_loop().
int
main(int argc, char *argv[])
{
    glview_initialize(GLVIEW_API_OPENGLES_11, &render);
    glview_register_initialize_callback(&initialize);
    glview_register_event_callback(&event);
    return glview_loop();
}

Now let's take a closer look at the callbacks.

Initialization callback

We register the initialization callback to initialize the square and display the virtual keyboard. This callback is called before Glview enters the main loop.
glview_register_initialize_callback(&initialize);
In initialize(), we first request virtual keyboard events so that we'll know when someone presses a key.
virtualkeyboard_request_events(0);
Next, we display the virtual keyboard on the screen:
virtualkeyboard_show();
The initialization code then uses calls to OpenGL ES functions to set up the display area, the background color and smooth shading, and initialize a simple orthographic projection for rendering:
unsigned int surface_width, surface_height;
glview_get_size(&surface_width, &surface_height);

glShadeModel(GL_SMOOTH);

glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

glViewport(0, 0, surface_width, surface_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glOrthof(0.0f, (float) (surface_width) / (float) (surface_height), 0.0f,
         1.0f, -1.0f, 1.0f);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glTranslatef((float) (surface_width) / (float) (surface_height) / 2, 0.5f,
             0.0f);

Display callback

We need to register a callback to render the square. This callback is mandatory for using Glview and is called frequently to refresh the graphics to ensure the smooth rotation of the square.

Our display callback is render() and we register it using glview_initialize(). We also specify the use of OpenGL ES version 1.1:
glview_initialize(GLVIEW_API_OPENGLES_11, &render);
To draw the square, we initialize an array of vertices and an array of colors for the vertices at the beginning of main.c:
static const GLfloat vertices[] = {
    -0.25f, -0.25f,
     0.25f, -0.25f,
    -0.25f,  0.25f,
     0.25f,  0.25f
};

static const GLfloat colors[] = {
    1.0f, 0.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 1.0f, 1.0f
};
We also define a variable angle, which denotes the rotation angle for the square:
static float angle = 0.0;
Our callback must first call glClear() to clear the color buffer:
glClear(GL_COLOR_BUFFER_BIT);
Then it enables the GL_VERTEX_ARRAY state and provides an array of vertices that describe our simple square:
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
Similarly, it enables the GL_COLOR_ARRAY state and provides an array that defines the color of each of our vertices:
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, colors);
Now it adds a rotation with respect to the vertical axis and then renders the square. The rotation angle has been initialized to 0 so that when the square is rendered initially, it doesn't rotate. As we'll see in the event callback code, the value of angle will change according to the keyboard input we receive.
glRotatef(angle, 0.0f, 1.0f, 0.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0 , 4);
Finally, we disable all client states that were used by the current rendering logic as it's generally a good practice to do so. This simple step can save you a lot of time in front of the debugger as EGL code is typically quite difficult to debug.
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);

Event callback

To handle user input from the screen, we register an event callback, in which we check each incoming event and respond to it. When a user presses a key, for example, a screen event is generated and Glview invokes the event callback we register.
glview_register_event_callback(&event);
The event callback handles two types of events: virtual keyboard events and screen events. When receiving a virtual keyboard event, it sets the keyboard_visible flag accordingly. It checks this flag later to determine whether to display the virtual keyboard.
if (virtualkeyboard_get_domain() == domain) {
    switch (code) {
    case VIRTUALKEYBOARD_EVENT_VISIBLE:
        keyboard_visible = true;
        break;
    case VIRTUALKEYBOARD_EVENT_HIDDEN:
        keyboard_visible = false;
        break;
    }
}
When a user touches anywhere on the screen or presses a key, a screen event is sent to the application.  On detecting a touch on the screen, we display the virtual keyboard if it isn't already visible.
switch (screen_val) {
    case SCREEN_EVENT_MTOUCH_TOUCH:
        if (!keyboard_visible) {
            virtualkeyboard_show();
        }
    break;
When a key is pressed, we receive a keyboard event and perform the desired action. For example, we can change the layout of the keyboard so that it's more suited to some function the user wants to perform, such as sending an email or entering a phone number.
case SCREEN_EVENT_KEYBOARD:
    screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_FLAGS, &screen_val);

    if (screen_val & KEY_DOWN) {
        screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_SYM,&screen_val);

        fprintf(stderr, "The '%c' key was pressed\n", (char)screen_val);

        switch (screen_val) {
        case KEYCODE_I:
            // Display the email layout with "Send" enter key
            virtualkeyboard_change_options(VIRTUALKEYBOARD_LAYOUT_EMAIL, VIRTUALKEYBOARD_ENTER_SEND);
            break;
        case KEYCODE_O:
            // Display the phone layout with "Connect" enter key
            virtualkeyboard_change_options(VIRTUALKEYBOARD_LAYOUT_PHONE, VIRTUALKEYBOARD_ENTER_CONNECT);
            break;
        case KEYCODE_P:
            // Display the default layout with default enter key
            virtualkeyboard_change_options(VIRTUALKEYBOARD_LAYOUT_DEFAULT, VIRTUALKEYBOARD_ENTER_DEFAULT);
            break;

On the screen, the layouts would look like this:

Diagram showing the default, phone, and email keyboard layouts.

You'll notice that there is an fprintf() statement in the code above. It sends the keyboard character that was pressed out to the console, in case you want to debug the application later.

We can also hide the virtual keyboard, which is useful when you want to use the full screen to display something. The user can pop up the virtual keyboard again by touching the screen.
case KEYCODE_H:
    // Hide the keyboard
    virtualkeyboard_hide();
    break;
Now we'll let the user control the rotation of the square using the a or z key. We define the rotation increment at the beginning of main.c:
#define ANGLE_INCREMENT 3.0f
#define CIRCLE_DEGREES 360.0f
The rotation angle is incremented each time a is pressed, and decremented each time z is pressed.
case KEYCODE_A:
    // Increment rotation angle
    angle = fmod(angle + ANGLE_INCREMENT, CIRCLE_DEGREES );
    break;
case KEYCODE_Z:
    // Decrement rotation angle
    angle = fmod(angle - ANGLE_INCREMENT, CIRCLE_DEGREES );
    break;
Recall that the Glview library calls our display callback render() to refresh the graphic display. The incremented or decremented value of the angle variable causes the square to rotate at an increased or decreased speed.
glRotatef(angle, 0.0f, 1.0f, 0.0f);

That's it! You can now build and run your application. Try playing with the keyboard to see how the application changes or changing the event callback to map different functions to different keys. You can also explore other callbacks that Glview supports and try adding a new callback!

Last modified: 2014-05-14



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

comments powered by Disqus