Showing posts with label Symbian. Show all posts
Showing posts with label Symbian. Show all posts

Monday, August 19, 2013

Introducing: Merry Cook


After a long hiatus since November 2011 I have released another clone of classic Russian handheld game from the '80s - Merry Cook. I knew that the "Nu, Pogodi!" code wasn't my top achievement, and I had to force myself into diving into it, but I feel it was worth it. Few things I think I did right this time:
  • Do not keep *any* game logic in QML. Qt has an excellent state machine framework, which makes writing the game logic in C++ relatively easy.
  • Keep the QML/C++ interface as simple as possible. Send signals from QML to C++ when user takes some action and update the QML UI from the C++ side by changing QProperties on some context property object. I've actually used two objects for that, because it made testing a bit easier.
  • Unit tests. I've set up the testing harness using gmock/gtest and I've used it to unit test some things. I probably would have been fine without them, since Merry Cook is a very simple but a) it forced me to divide stuff into more manageable classes and b) it gave me a sense of accomplishing something early. It's funny, because even though I'm absolutely conscious of the latter fact, I think it gave me enough boost to get to the point where I had moved forward with implementation and polishing, because I really wanted to publish this game.
  • QProperty helper. I wrote an abominable macro for reducing the QProperty boilerplate:
Things still on my TODO list:
  • More tests. Besides unit tests I'd also like to write some integration tests for the state machine setup and connections, but I didn't have time to think how this should be done without making too much state public just for testing. Maybe next time.
  • Refactor "Nu, Pogodi!". I jumped straight into new project, but I should have started with refactoring the old crap. On the other hand, it might have sucked out all the motivation out of me, and had I done it, I wouldn't have been writing this post right now. So, maybe next time.
  • Passing enums to QML. I have no idea what I did wrong, but I couldn't get the QML to see my C++ enums. I've resorted to passing them as simple ints and using magic numbers on QML side, but it's definitely something I should fix. Obviously not now, but next time.
Anyways, I'm really happy with the final results, especially with the gameplay experience, which I think mimics the original game very well. Try it yourself!

Wednesday, October 3, 2012

Screensaver blocking on Symbian

In my latest game the players spend significant amount of time just watching the screen and trying to figure the puzzle out. The first few levels are obvious and most players will sole them in a few seconds,m but as the difficulty of the puzzles increases the players stare at the screen longer and longer. At some point the screensaver would kick in and piss the player off (fortunately my ragtag QA/betatester team found this issue before launch).

Google search results are (as usual) helpful, but the most promising lead, the QSystemScreenSaver class is not a solution. There are three problems with it:

  1. The API of the class itself is terrible.
  2. The API of the related QML element is even worse.
  3. Last, but not least, it doesn't work (at least not in the Qt Mobility version shipped with the Qt SDK).

(BTW: these three points sums up pretty much every experience with Qt Mobility package I had. Qt devs should either kill this festering boil with fire or fix it and rename it, because I learned to dread everything remotely related to Qt Mobility, and I suspect I'm not the only one).

Anyways, let's get back to the core of the problem, i.e. "how to block the screensaver". Qt Mobility failed, but the task doesn't seems like a rocket science to me. Slightly different Google search suggested using native Symbian's User::ResetInactivityTime() method. Few minutes and one QTimer later, everything worked:
#ifndef SCREENSAVERBLOCKER_H
#define SCREENSAVERBLOCKER_H

#include <QObject>
#include <QApplication>
#include <QTimer>

class ScreenSaverBlocker : public QObject
{
    Q_OBJECT

public:
    explicit ScreenSaverBlocker(QObject *parent = 0) : QObject(parent) {
        mTimer.setInterval(1000);
        connect(&mTimer, SIGNAL(timeout()), this, SLOT(blockScreenSaver()));
        changeScreenSaverState(QApplication::activeWindow() != 0);
        if (qApp) {
            qApp->installEventFilter(this);
        }
    }

    void changeScreenSaverState(bool blockScreenSaver) {
        if (blockScreenSaver && !mTimer.isActive()) {
            mTimer.start();
        } else {
            mTimer.stop();
        }
    }

protected:
    bool eventFilter(QObject *obj, QEvent *event) {
        Q_UNUSED(obj)
        if (event->type() == QEvent::ApplicationActivate
         || event->type() == QEvent::ApplicationDeactivate) {
            changeScreenSaverState(event->type() == QEvent::ApplicationActivate);
        }
        return false;
    }

private slots:
    void blockScreenSaver() {
#ifdef Q_OS_SYMBIAN
        User::ResetInactivityTime();
#endif
    }

private:
    QTimer mTimer;
};

#endif // SCREENSAVERBLOCKER_H
The important thing in the code above is watching the ApplicationActivate and ApplicationDeactivate events - after all, when your app is in background, you shouldn't affect the phone behavior. I'm not sure if the app would fail the Nokia's QA process without this feature, but it seemed prudent to write the code this way.

If you want to use this object in your QML UI just register it with qmlRegisterType and add the registered import and QML element to your root element.

Wednesday, September 12, 2012

Screen orientation and QT applications on Symbian Belle

Let's take a break from Android gotchas and do some mobile necrophilia, i.e. let's talk about Symbian.

Recently I received an email from Nokians saying that they are testing Nokia Store content with new firmware update and my app won't work after update. After few quick email exchanges we narrowed down the problem to screen orientation locking code I wrote about some time ago. It turns out that things can be done much simpler:

Window {
    Rectangle {
        id: root
        anchors.fill: parent

        Component.onCompleted: {
            screen.allowedOrientations = Screen.Landscape
        }

        // more stuff
    }
}
int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));

    QmlApplicationViewer viewer;
    viewer.setMainQmlFile(QLatin1String("qml/nupagadi/GameArea.qml"));
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationLockLandscape);
    viewer.setResizeMode(QDeclarativeView::SizeRootObjectToView);
    viewer.showExpanded();

    return app->exec();
}
Less code, no need to comment it as a gotcha/workaround, and it's supposedly futureproof.

I'm very positively surprised with Nokians' approach, responsiveness and this whole experience. Of course I wouldn't be me if I didn't bitch a little bit, namely: why did I have this problem in the first place? I mean, locking the screen orientation is not a rocket science and should be well documented. It should, but unfortunately it's not, like so many things about QML.

Friday, March 23, 2012

Word Judge for Symbian available in Nokia Store

Today I have finally pushed both versions of Word Judge through Nokia Store QA. The version with an english dictionary was available for over the week now, but I had to submit the polish version few times until it passed the Nokia tests.

Anyways, the applications are available for free in Nokia Store. Here's the link for english version:


And for polish version (might not be available in all countries - I had to add some restrictions to pass QA process):


Stay tuned for posts about development of Symbian version and enjoy your Scrabble games.

Sunday, January 22, 2012

How (not to) test your QML application for Symbian

First of all by QML I do not mean this, I mean this: a UI module of Qt, the cross-platform framework. The gals and guys at Nokia figured out that modern user interface cannot be fully described by static layout in a XML file. Microsoft figured out that too, but they chickened out and only extended XML a bit and added an 'a' to file extension to make it look like something new. Nokians took a step further and created new language for declarative UI based on JavaScript called QML.

The QML UI components can be defined in two ways: it can be a QML file composed of other components (for instance that's the usual way to define the main UI file) or it can be a C++ extension. Both ways can be used together to create a plugin, which can be imported to your project.

And at last we reach the intended topic of this post: testing. What happens if some QML file defining a component is missing? What happens if the whole plugin is missing or the version of this plugin is lower than the one required by application? QML files are interpreted during runtime, so of course you get the runtime error. In the best scenario it limits the functionality of your app, in the worst case it renders it completely unusable.

But hey, you can catch most of those errors simply by clicking through your application, right? Not exactly, doing so only tells you that in works on one particular device. You might have some plugins already installed, but not included in application's package and your app will work only on the devices which happen to have those plugins, which is not very likely.


That's exactly the error I made when I published the first version of "Nu, Pogodi!". I submitted for Q&A process an application with dependencies to Qt Components 1.1, build with the latest Qt SDK. I've tested it thoroughly on some devices I had access to and via Remote Device Access service (which BTW rocks; I wish there was a similar service for Android) and everything worked fine. The application was rejected by Q&A, because at the end of 2011 there was some technical issues with Nokia Store and latest Qt and I was told to rebuild my application with old SDK, which included only Qt Components 1.0. I've tested my game again and everything worked so I published it to Nokia Store. Few days later I received first reviews - all negative, along the lines "doesn't work, beware".

Qt Smart Installer partially prevents those errors, but you still might shoot yourself in the foot in some cases. My game had dependencies to Qt Components 1.1, but the pkg file declared dependency to version 1.0, because it was created with old SDK. When my customers installed the game, the smart installer ensured only that version 1.0 is installed, but my game needed newer version and failed during runtime. I didn't caught this during testing, because all of my devices had latest Qt Components installed.

That was the "How not to test your QML application" part, now let's get to solution. It's really simple: downgrade all the stuff needed by your application to versions defined in pkg file. To check the current versions of Qt libraries and plugins I recommend using an excellent QtInfo tool. To downgrade Qt you need the sis files distributed with old Qt SDK versions.

This simple steps should ensure that your application will work properly on all supported devices. Nevertheless, you should prepare for failure and handle all runtime errors in a user friendly way. But that's the topic for another post...