Showing posts with label mobile application. Show all posts
Showing posts with label mobile application. Show all posts

Saturday, February 1, 2014

Handling GamePad events in BB10 Cascades App

I added GamePad support to my CrazyFlight game for BB10. You can see demo here.



In this post I will describe, how we can add GamePad support to BB10 cascades or Qt app.

First we should add use_gamepad permission to bar-descriptor.xml file. This is not necessary to enable gamepad support but its necessary for AppWorld to detect that your game supports GamePad, this helps in app discovery process.
 <permission>use_gamepad</permission>
We should also add libscreen dependency to our .pro file.
LIBS += -lscreen
As far as I know there is no Cascades API for handling GamePad events, we need to rely on native API to get GamePad events. I created a Helper class that handles Native API call back and send events to Cascades QML items.

Here is definition of my helper class's (GamePadObserver.h) header file.
#ifndef GAMEPADOBSERVER_H_
#define GAMEPADOBSERVER_H_

class GamePadObserver: public QObject {
 Q_OBJECT
 Q_ENUMS(GamePadButton)

 // Structure representing a game controller.
 struct GameController {
  // 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];
 };

public:
 //Enum which we will use to send signal when GamePad event is detected
 enum GamePadButton{
   A_BUTTON=0,
   B_BUTTON,
   C_BUTTON,
   X_BUTTON,
   Y_BUTTON,
   Z_BUTTON,
   MENU1_BUTTON,
   MENU2_BUTTON,
   MENU3_BUTTON,
   MENU4_BUTTON,
   L1_BUTTON,
   L2_BUTTON,
   L3_BUTTON,
   R1_BUTTON,
   R2_BUTTON,
   R3_BUTTON,
   DPAD_UP_BUTTON,
   DPAD_DOWN_BUTTON,
   DPAD_LEFT_BUTTON,
   DPAD_RIGHT_BUTTON,
   NO_BUTTON
 };

public:
 GamePadObserver(QObject* parent = 0);
 virtual ~GamePadObserver();

 //Main event loop should send event to this handler if it can not handle event by itself
 //This handler will try to handle event if its related to GamePad
 void handleScreenEvent(bps_event_t *event);

signals:
        //Signals will be emitted when Gamepad events is detected
 void buttonReleased(int button);
 void buttonPressed(int button);

private:
 //Helper methods to discover the GamePad and device connection
 void discoverControllers();
 void initController(GameController* controller, int player);
 void loadController(GameController* controller);
 void handleDeviceConnection(screen_event_t screen_event);
       
 // Methods to handle gamepad events
 void handleGamePadInput(screen_event_t screen_event);
 QString gamePadButtonAsString(GamePadButton button);

private:
 screen_context_t _screen_ctx;
 GameController _controllers[2];
 bool _conneted;
 GamePadButton _lastButton;
};
#endif /* GAMEPADOBSERVER_H_ */
Now let's see source file.
In constructor we are creating screen context and then trying to discover if there is GamePad connected to device already.
#define SCREEN_API(x, y) rc = x; \
    if (rc)  printf("\n%s in %s: %d, %d", y, __FUNCTION__,__LINE__, errno)

GamePadObserver::GamePadObserver( QObject* parent)
: QObject(parent),_screen_ctx(0),_conneted(false)
{
 // 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");
 discoverControllers();
}

void GamePadObserver::discoverControllers()
{
    // Get an array of all available devices.
    int deviceCount = 0;
    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 controllerIndex = 0;
    for (int 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);
}
On destructor we should release screen context.
GamePadObserver::~GamePadObserver()
{
 screen_destroy_context(_screen_ctx);
}
loadController setup our GameController structure, that we will use to store currently pressed buttons while handling the events.
void GamePadObserver::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);
        qDebug() << "Gamepad device ID" <<  controller->id;
    } else {
        sprintf( controller->deviceString, "Joystick device: %s", controller->id);
        qDebug() << "Joystick device ID" <<  controller->id;
    }
}
handleScreenEvent function should be called from main event loop when event is related to screen domain. This function check if event type is GamePad device connection or its GamePad button event and handle it accordingly.
void GamePadObserver::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:
        {
         handleGamePadInput(screen_event);
         break;
        }
        case SCREEN_EVENT_DEVICE:
        {
         // A device was attached or removed.
         handleDeviceConnection(screen_event);
         break;
        }
    }
}
handleDeviceConnection handles GamePad device connection. If it detects new connection then it loads new device, in case of device disconnection it remove device.
void GamePadObserver::handleDeviceConnection(screen_event_t screen_event)
{
    // 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;
            }
        }
    }
}

void GamePadObserver::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);
}
handleGamePadInput function that handles GamePad events.
void GamePadObserver::handleGamePadInput(screen_event_t /*screen_event*/)
{
    int i;
    for (i = 0; i < MAX_CONTROLLERS; i++) {
        GameController* controller = &_controllers[i];

        if ( controller->handle ) {
            GamePadButton gamePadButton = NO_BUTTON;

            // 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");
            }

            for(int i = A_BUTTON ; i < NO_BUTTON ; ++i) {
             if( controller->buttons & (1 << i) ) {
              gamePadButton = (GamePadButton)(i);
              break;
             }
            }

            if( gamePadButton == NO_BUTTON ) {
             emit buttonReleased( _lastButton );
             _lastButton = NO_BUTTON;
            }
            else if( _lastButton != gamePadButton ) {
             emit buttonReleased(_lastButton);
             emit buttonPressed(gamePadButton);
             _lastButton = gamePadButton;
            }
        }
    }
}
Now we have all necessary implementation to handle the GamePad connection and GamePad button events. But we should pass events related to GamePad to our helper class when we receive it in our main event handler. To do that, In our main function we should register main bps event loop or filter as below and can use GamePadObserver as described.
#include "GamePadObserver.h"

static QAbstractEventDispatcher::EventFilter previousEventFilter = 0;
static GamePadObserver _gamePadObserver;

static bool bpsEventFilter(void *message)
{
    bps_event_t * const event = static_cast(message);

    if( event && bps_event_get_domain(event) == screen_get_domain()) {
     _gamePadObserver.handleScreenEvent(event);
    }

    if (previousEventFilter)
        return previousEventFilter(message);
    else
        return false;
}

int main(int argc, char **argv)
{
    // Register GamePadObserver so we can use it in QML code
    qmlRegisterUncreatableType("GamePadObserver", 1, 0,"GamePadObserver", "");

    QApplication app(argc, argv);

    previousEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(bpsEventFilter);

    QScopedPointer view(new QDeclarativeView());
    view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    view->setResizeMode( QDeclarativeView::SizeRootObjectToView );

    QDeclarativeContext *ctxt = view->rootContext();
    view->setSource(QUrl("app/native/assets/main.qml"));

    ctxt->setContextProperty("GamePad",&_gamePadObserver);
    view->showFullScreen();

    return app.exec();
}
And finally, out GamePadObserver class is ready to deliver GamePad events. To handle GamePad events in QML you can use GamePadObaserver class as below.
        
        Connections{
            target: GamePad
            onButtonPressed: {
                                
                if(button == GamePadObserver.DPAD_UP_BUTTON
                || button == GamePadObserver.DPAD_DOWN_BUTTON ){
                    handleUpButton();
                }else if(button ==  GamePadObserver.DPAD_LEFT_BUTTON 
                || button ==  GamePadObserver.DPAD_RIGHT_BUTTON) {
                    handleLeftButton();
                }          
                else if(button == GamePadObserver.MENU1_BUTTON                 
                || button == GamePadObserver.MENU2_BUTTON
                || button == GamePadObserver.X_BUTTON
                || button == GamePadObserver.Y_BUTTON 
                || button == GamePadObserver.A_BUTTON 
                || button == GamePadObserver.B_BUTTON ) {
                    handleButtonSelection();
                } 
            }        
        }
So, I hope this will help to utilize GamePad events in your games.

Sunday, April 14, 2013

Audiobook Reader for Ubuntu-Touch

I have been exploring and working on Ubuntu-Touch for some time now. I have wrote some QML code for official Ubuntu-Touch calendar app and mean time I was also working on porting my Audiobook Reader application. This porting exercise helped me to understand how mature SDK is and also helped me to learn the SDK.

There were some minor issue with Ubuntu-Touch SDK, but nothing serious that can affect the work. As per Ubuntu-Touch development guideline, they prefer QML/Javascript and suggest to avoid C++ code as much as possible. This will help to make sure most of application is compatible for most Ubuntu platform. I tried to follow guideline and tried to not use C++ till now for app. I found myself missing C++ now and then, sometimes there are plugins that we can use instead and sometime there is no alternative. Mostly I missed the File IO, surely SDK team will come up with something, but till now there is no solution.

Anyway, I did not faced any major issue and was able to create initial version of Audiobook Reader quite easily without much problem. Following is demo.


Friday, November 9, 2012

Porting Qt app to BlackBerry 10 SDK

Recently BB10 device is getting in to news for its Qt framework support. I got curious about it and decided to port my Harmattan Qt app to BB10 device. First of all I am glad that finally there will be a real main stream device that will support Qt as development framework.

After installing BB10 NDK and going through few sample application I realized that I need to create BlackBerry Cascades C++ project using its QNX Momentics IDE event though I just wanted to create plain Qt application. So I created the Cascades C++ project and merged my existing Qt App's code to BB10 project.

I was aware that in order to run the application I need to change Harmattan component used in QML to standard QML component but I wanted to check debugging support provided by Momentics IDE to debug Qt cpp code and QML code. So I started application and I was staring at while screen with no error message in console of IDE and seems like there is no support to debug QML code or Java script code. I added few debug message to identify problem but still did not see any message in console. I still don't know how to see those debug log from Qt app in BB10. I think there must be some installation problem.

In addition to this debug log problem, Its emulator is not working in normal mode on my HP Elitebook 6930p laptop with Ubuntu as OS. I always have to run it in safe mode.  While running it in Safe mode, I faced another major problem. The emulator spill out of my laptop's 15 inch screen. Default emulator resolution is so big, I have to scroll a lot to see the whole screen of device. It seems that controller utility provided with emulator is not supposed to work when emulator is running in safe mode. This is so discouraging. However I decided continue my porting effort.

So no debug log and huge emulator that my 15 inch laptop can not contain, I decided to first try to run my application using Qt Desktop version and once application is working fine on desktop Qt SDK. I merged my code back to BB10 project.

I needed to make some minor change relate to path before I can see anything running. In BB10, Qt application locate images and QML file in assets folder not from Qt's Resource file. So I made necessary changes to use Image and QML file from Assets folder. Actually you can specify where your image files and QML files are present in bar-descriptor.xml but I am using default assets folder only.

Following is how you can show QML file located in assets folder using QDeclarativeView.

    QScopedPointer view(new QDeclarativeView());
    view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    view->setResizeMode( QDeclarativeView::SizeRootObjectToView );
    view->setSource(QUrl("app/native/assets/main.qml"));
    view->showFullScreen();

So after this I was able to see my application in emulator. But my application was supported in landscape mode only so I need to make change to make it launch in landscape mode only. To make application support either Portrait or Landscape mode, you need to set aspectRatio tag in bar-descriptor.xml, and to disable auto orientation change you can set autoorients tag to false

Following is my entry into bar-descriptor.xml
    
<initialwindow>
    <aspectratio>landscape</aspectratio>
    <autoorients>false</autoorients>
    <systemchrome>none</systemchrome>
    <transparent>false</transparent>
</initialwindow>

So now my plain Qt Application is running fine in emulator, but my application keeps running even after its minimized. I realized that I need to capture BPS Event in order to detect application minimize and maximize event. I found good information here for this purpose.

Following is my code to detect application minimize event so I can pause my application.

static QAbstractEventDispatcher::EventFilter previousEventFilter = 0;

static bool bpsEventFilter(void *message)
{
    bps_event_t * const event = static_cast<bps_event_t>(message);

    if (event && bps_event_get_domain(event) == navigator_get_domain()) {
        const int id = bps_event_get_code(event);

		//unsigned int code = bps_event_get_code(event);
		switch ( id ) {

		case NAVIGATOR_WINDOW_INACTIVE:
		   qDebug() << "INFO: Window inactive";
		   break;

		case NAVIGATOR_WINDOW_ACTIVE:
		   qDebug() << "INFO: Window active";
		   break;

		case NAVIGATOR_WINDOW_STATE:
		   navigator_window_state_t state = navigator_event_get_window_state(event);
		   if (state == NAVIGATOR_WINDOW_FULLSCREEN) {
			   qDebug() << "INFO: Resume game";
		   } else {
			   qDebug() << "INFO: Pause game";
			   Utils::instance()->PauseGame();
		   }
		   break;
		}
    }

    if (previousEventFilter)
        return previousEventFilter(message);
    else
        return false;
}

This is how you add event handler in main function.
 previousEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(bpsEventFilter);
So After all this my porting activity is almost complete. Now I need to learn how to submit application to BB10 store. Let's see how that goes.  So overall I feel BB10 provides nice Qt support for app development, emulator support needs to be improved though to be considered as useful ( at lest in my case).

Wednesday, February 24, 2010

Google Shooper application

Google announced android application called Google Shopper. This application lets you find product information quickly by using your phone's camera. It can recognize cover art of books, CDs, DVDs, and video games, along with most barcodes. You can also speak the name of the product you're looking for. It can tell price of item , places from where it can be purchased. It also saves your history so you'll always have product and price information even when you don't have a signal.

Here is demo video from google.