Showing posts with label port. Show all posts
Showing posts with label port. Show all posts

Qt: Write once, #ifdef everywhere?

Despite what the title of this post might suggest, I really like Qt. But as a developer, I also know that "write once, run everywhere" isn't realistic without writing some special-cased platform-specific code. Qt does take care of many platform-specific things, and I think it's the closest you can get to "write once, run everywhere" right now.

This post should serve two purposes: To provide a real-world example of what needs to be done in the code for the app to work on both Maemo 5 and Symbian^3 (with example code), and to get suggestions on what parts I could rewrite in a more platform-agnostic manner with existing APIs (so please comment if you are in the know!).

Here's the story: In early September, I've rewritten my game "That Rabbit Game" to use QGraphicsView on the N900, and in the last weeks, I've ported it to Symbian^3. I use the macro Q_OS_SYMBIAN to check for Symbian and Q_WS_MAEMO_5 to check for Fremantle. Here's the current main menu on a N900 and N8 (different screen resolutions and aspect ratios):

Qt modules: In the qmake project file, I can use linux-g++-maemo5 to add Maemo-specific configuration, and symbian for Symbian-specific settings. I use D-Bus module on Maemo, but obviously not on Symbian, so my project file contains something like this:

linux-g++-maemo5 {
QT += dbus
}

This is nice, because I only have to maintain one project file, and the block structure is very readable (and I can even use different Qt submodules for each platform).

Screen orientation: Symbian has auto-rotation for Qt apps by default, and Maemo 5 has landscape-only mode by default. For my game, portrait mode does not make sense, so I have to request landscape-only on Symbian (similarly, if I want auto-rotation everywhere, I have to request it on Maemo 5 and do nothing in Symbian). For this specific case, I need some libs in Symbian, so I add this to the project file:

symbian {
LIBS += -lcone -leikcore -lavkon
}

I also need to add this to the top of my main source file:

#ifdef Q_OS_SYMBIAN
#include <AknAppUi.h>
#endif

And finally, I have to copy'n'paste a code block into my main() function before I create the first window:

#ifdef Q_OS_SYMBIAN
CAknAppUi* appUi = dynamic_cast<CAknAppUi*> (CEikonEnv::Static()->AppUi());
TRAPD(error,
if (appUi) {
// Lock application orientation into landscape
appUi->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape);
}
);
#endif

It would be nice if Qt (or Qt Mobility?) has some generic API for this, where I just need to do something along the lines of QRotation::setMode(QRotation::LandscapeOnly); and let it take care of the platform-specific stuff.

Accelerated QGraphicsView: On Symbian^3, QGraphicsView is automatically accelerated via OpenVG (I think), but on Maemo 5, it can use OpenGL for this (but does not by default), so in the code where I create my QGraphicsView, I have to have this at the top:

#ifdef Q_WS_MAEMO_5
# include <QGLWidget>
#endif

And then somewhere down that file where I create the QGraphicsView, this code goes there:

#ifdef Q_WS_MAEMO_5
QGLWidget *glw = new QGLWidget(QGLFormat(QGL::DoubleBuffer));
view->setViewport(glw);
#endif

It's not that difficult, and is already cross-platform (on platforms where OpenGL is available), but maybe QGraphicsView can be OpenGL-accelerated on Maemo 5 by default. Maybe there is some non-obvious side effect that using an OpenGL viewport has that I'm not aware of, and that's why one has to do it manually.

Accelerometer readings: This might actually be a bug in Qt Mobility. For my game, I have to read the accelerometer's Y axis on Symbian, and its X axis on Maemo to determine the rotation for the same holding position of the device, so there's another platform-specific #ifdef there. Again, this will hopefully be fixed in a future release of Qt Mobility, and will most likely be fixed for MeeGo as well.

Task switcher: It's customary on Maemo to have a task switcher button in the upper left corner. There's no platform-agnostic way of going to the task switcher, so I needed to special-case it for Maemo 5, and have not bothered implementing it on Symbian^3 (suggestions welcome!). First, I need to include D-Bus headers for this (I've already mentioned how to request the D-Bus module in the qmake project file above) :

#if defined(Q_WS_MAEMO_5)
# include <QDBusConnection>
# include <QDBusMessage>
#endif

When I'm in the handler code where I need to do the actual task switching, I can utilize it to activate Maemo 5's task switcher:

#if defined(Q_WS_MAEMO_5)
QDBusConnection c = QDBusConnection::sessionBus();
QDBusMessage m = QDBusMessage::createSignal("/", "com.nokia.hildon_desktop", "exit_app_view");
c.send(m);
#endif

This is very much Maemo 5-specific, and will very likely not work on MeeGo Harmattan. Again, here it would be nice to have a cross-platform way of doing window management (in Qt or Qt Mobility?). I can imagine this being useful not only on handsets, but also on netbooks and tablets (from a MeeGo PoV). Again, trying to come up with pseudo-APIs here, QWindowManager::showTaskSwitcher(); could be a nice way to handle this in a cross-platform way, hiding platform-specific implementations. From what I've seen of MeeGo Touch, activating the task switcher there simply iconifies the window on the Desktop, so maybe if I would iconify my main window, it should activate the task switcher on Maemo 5 and Symbian. It does not work on Maemo 5 right now, though, and I haven't tested it on Symbian.

Screen resolution: This was (thanks to QGraphicsView) less of a problem than I thought it would be. Symbian^3 (or at least the N8) uses 640x360 as its resolution, and Maemo 5 uses 800x480. Maybe the MeeGo Harmattan device will use yet another resolution. I still configure my QGraphicsScene object manually via #ifdefs to get a good resolution, but the code could just measure the resolution and configure the scene as well. Here's what I use:

#if defined(Q_WS_MAEMO_5)
setSceneRect(0, 0, 800, 480);
#elif defined(Q_OS_SYMBIAN)
setSceneRect(0, 0, 640, 360);
#endif

I then use the sceneRect() of my scene to calculate a scale factor for all contents and call setScale() on all root items of the scene to scale the contents to the current screen size. You might have to take care of different aspect ratios on different devices, too - but it was unproblematic for my use case (the game) this time.

Brain Party ported to the N900

Brain Party from Hudzilla Games has been released as open source for Linux some days ago. A thread on t.m.o suggested somebody port this over to Maemo. An afternoon of tinkering later (and with the help of Javispedro's SDL-GLES library), I've got the game to run on the N900 (see the patch for details on the changes - as you can see, the original source code is pretty clean, so porting was mostly straightforward after being told about SDL-GLES, fixing a parameter for glTexImage2D and doing some math to scale the game to the N900's screen and scale the input coordinates the same way). The package is currently in Extras-Devel, soon to be promoted to Extras-Testing. In the mean time, you can grab binary and source packages from this page (use apt-get -f install afterwards to satisfy dependencies).

Please test it and vote for it on the Maemo.org package page when it enters Extras-Testing (did you know that you can follow @maemoextras on Twitter to see packages entering Extras-Testing and Extras? Now you do..).

It's good to see developers porting their games to Linux and open sourcing them, which makes learning from their code and porting the games to other platforms very easy. If you like the game, please support the developers by purchasing Brain Party for the iPhone (you probably don't have an iPhone, so show the N900 game off to your freedom-hating friends and tell them that they should purchase the game for their iPhone/iPod touch on the App Store). The game is also available for Windows Mobile and Xbox 360.

MaemoPad+ ported to Desktop Linux

Some weeks ago, a user of MaemoPad+ asked me if it was possible to port MaemoPad+ to the Desktop, so users of Tablet PCs can make use of it. Apart from that, this can also be useful for users of MaePad to copy their "memos.db" file from the N900 to their PC and edit the file (or just view the contents) on the big screen. The file format used by both MaemoPad+ and MaePad is the same, so users of the mobile versions can share their database files with the Desktop version (and vice versa), which by the way looks like this at the moment:

Screenshot of MaemoPad+ on the Desktop

Most of the Maemo 4 libraries are readily available in Debian (hildon, libosso, hildon-icons, etc..), so the initial port has not been too difficult, even though proper Desktop integration obviously needs more work, and there are some ugly crashers still hidden inside the code ;)

I wonder if it's possible to also package Maemo 5 libraries (Hildon 2.2, etc..) for Debian and upload them to the Debian repositories - this should make it easier to port Maemo 5 applications to the Linux Desktop without having to re-write the whole UI layer. Another cool thing would be to have Hildon 2.2 for Diablo, which would allow us to "backport" Maemo 5 apps to Maemo 4 - again, without the work of having to re-write the UI.

Packages for Ubuntu are available from the MaemoPad+ PPA, and the source is available via Git. Please send backtraces of crashes or (even better!) patches against the "desktop" branch in the Git repo.