Anmelden Registrieren

Badges

Follow Peter Bouda on Google Plus

Feeds

Neueste BlogeinträgeBlog

Qt und Asteroids-Demo für Dingux auf dem Dingoo A320

Bearbeitet am Sonntag, 25. April 2010, 10:58 Uhr von pbouda

In diesem Tutorial zeige ich euch, wie ihr die Qt-Bibliotheken für Dingux auf dem Dingoo A320 kompiliert und wie eine Anpassung eine der Demo-Anwendungen ausschaut, damit sie auf der Spielekonsole läuft. Als Demo-Anwendung habe ich mir das Spiel Asteroids ausgesucht, damit haben wir gleich ein nettes, kleines Spiel auf dem Gerät verfügbar. Das Spiel basiert zwar auf dem Qt3-Kompatibilitätsbibliotheken von Qt und damit auf einem veralteten GraphicView, für unsere Zwecke sind aber hauptsächlich die veränderte Grafikauflösung sowie Tastatureingabe (per Buttons) interessant. Diese Anpassungen lassen sich ohne Probleme auf die GraphicsView/-Scene Kombination in Qt 4 übertragen. Ich stelle hier außerdem die vorkompilierten Bibliotheken sowie den angepassten Quellcode samt Kompilat der Asteroids-Anwendung für Dingux zur Verfügung. Und wer noch keinen Dingoo hat: unbedingt hier bestellen.

YouTube-Video Qt-Asteroids auf dem Dingoo A320

Was ist Dingux?

Dingux oder auch Dingoo Linux ist ganz einfach ein Linux-basiertes Betriebssystem für die Spielekonsole Dingoo A320. Der chinesische Hersteller hatte mit dem Gerät eine komplette GCC Toolchain veröffentlicht, um Spieleentwickler anzuziehen. Natürlich hat sich schnell jemand daran gemacht, den Linux-Kernel für das Gerät zu übersetzen (Webseite des Dingoo Linux Projekts). Und da das Gerät einen MIPS-Prozessor besitzt, der u.a. auch von Qt Embedded unterstützt wird, können die Qt-Bibliotheken für dieses System relativ unkompliziert gebaut werden. Danach laufen die meisten eurer Qt-Anwendungen auf dem A320, natürlich müsst ihr mit einer relativ kleinen Bildschirmauflösung und den begrenzten Eingabemöglichkeiten leben. Am Beispiel des Spiels Asteroid zeige ich euch weiter unten, wie ihr eine Anwendung an das Gerät anpassen könnt, damit Bildschirmauflösung und Eingabe passen.

Als Grundlage müsst ihr auf jeden Fall zunächst einmal Dingux auf den Dingoo installieren. Die beste Anleitung dazu habe ich hier gefunden. Ist für Windows-Benutzer, unter Linux funktioniert es aber recht ähnlich und ich überlasse es dem geneigten Leser, sich damit auseinanderzusetzen. Ein deutschsprachiges Forum über Dingux findet ihr hier.

Wenn ihr alles erfolgreich durchgeführt habt, solltet ihr ein bootendes Linux auf eurem Gerät haben:

Qt Embedded für Dingux kompilieren

Die Qt-Bibliotheken lassen sich erstaunlich unkompliziert für Dingux kompilieren. Basis ist dabei das früher so genannte “Qt Embedded”, ein Qt für Betriebssysteme ohne X Server. Unter Linux benutzt Qt normalerweise den X Server zur Darstellung der Fenster, für andere Linux- und Unix-System werden diese direkt in den Grafikspeicher geschrieben. So soll es in unserem Fall auch auf dem Dingoo funktionieren. Als System zum Kompilieren habe ich ein Ubuntu Linux verwendet, im Prinzip sollte es aber auch unter Windows und Mac OS möglich sein die Bibliothek für den MIPS-Prozessor zu kompilieren. Am Ende dieses Abschnitts findet ihr außerdem vorkompilierte Bibliotheken zum einfachen Kopieren auf die Speicherkarte.

Qt Embedded ist mittlerweile kein eigenes Paket mehr, es muss nur das Qt-Everywhere-Paket heruntergeladen werden. Wir benutzen hier Version 4.6.2, diese entpackt ihr einfach in ein Verzeichnis eurer Wahl. Außerdem braucht ihr die oben erwähnte GCC Toolchain für den MIPS-Prozessor des Dingoo, diese gibt es hier zum Herunterladen (ich habe dingux_toolchain_20091022.tar.bz2 heruntergeladen). Auch dieses Paket müsst ihr entpacken und an einen Ort eurer Wahl kopieren. Von den Dingux-Leuten wird der opt-Ordner vorgeschlagen, bei mir residiert alles unter /opt/mipsel-linux-uclibc.

Bevor wir Qt kompilieren können sind zwei Änderungen am Qt-System und -Quellcode nötig. Zunächst heißen die mkspecs standarmäßig mips, unsere Toolchain heißt aber mipsel. Das lässt sich schnell beheben: im entpackten Qt-Verzeichnis findet ihr den Ordner qt-everywhere-opensource-src-4.6.2/mkspecs/qws/linux-mips-g++. Diesen solltet ihr umbennen oder kopieren, so dass er linux-mipsel-g++ heißt. In diesem Ordner findet ihr die Datei qmake.conf, in dieser müsst ihr alle Präfixe mips durch mipsel ersetzen. Die Datei sieht dann so aus:

#
# qmake configuration for building with mipsel-linux-g++
#

include(../../common/g++.conf)
include(../../common/linux.conf)
include(../../common/qws.conf)

# modifications to g++.conf
QMAKE_CC                = mipsel-linux-gcc
QMAKE_CXX               = mipsel-linux-g++
QMAKE_CFLAGS           += -mips32
QMAKE_CXXFLAGS         += -mips32
QMAKE_LINK              = mipsel-linux-g++
QMAKE_LINK_SHLIB        = mipsel-linux-g++

# modifications to linux.conf
QMAKE_AR                = mipsel-linux-ar cqs
QMAKE_OBJCOPY           = mipsel-linux-objcopy
QMAKE_STRIP             = mipsel-linux-strip

load(qt_config)

Als Zweites müssen wir eine Änderung der internen Qt-Klasse QFSFileEnginePrivate durchführen. Diese scheint unter Dingux Probleme mit dem Aufruf der Betriebssystemfunktion realpath zu haben. Ich konnte nicht definitiv herausfinden, woran das liegt, vermute aber, dass das Problem mit dem VFAT-Dateisystem auf der Dingux-Speicherkarte zu tun hat. Der Aufruf von realpath scheint deswegen fehlzuschlagen, weil ein ext-Dateisystem erwartet wird. Für nicht-Linux-System bringt Qt aber eine Alternative Implementierung mit, wir müssen nur dafür sorgen, dass diese auch verwendet wird. Dazu öffnet ihr die Datei qt-everywhere-opensource-src-4.6.2/src/corelib/io/qfsfileengine.cpp und kommentiert die Zeilen 148 bis 153 in der Funktion QFSFileEnginePrivate::canonicalized aus, nach #if defined(Q_OS_LINUX) || defined(Q_OS_SYMBIAN) und vor #endif. Der entsprechende Codeabschnitt sieht dann so aus:

QString QFSFileEnginePrivate::canonicalized(const QString &path)
{
    if (path.isEmpty())
        return path;

    // FIXME let's see if this stuff works, then we might be able to remove some of the other code.
#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
    if (path.size() == 1 && path.at(0) == QLatin1Char('/'))
        return path;
#endif
    // Mac OS X 10.5.x doesn't support the realpath(X,0) extenstion we use here.
#if defined(Q_OS_LINUX) || defined(Q_OS_SYMBIAN)
    /*char *ret = realpath(path.toLocal8Bit().constData(), (char*)0);
    if (ret) {
        QString canonicalPath = QDir::cleanPath(QString::fromLocal8Bit(ret));
        free(ret);
        return canonicalPath;
    }*/
#endif

    QFileInfo fi;
    const QChar slash(QLatin1Char('/'));
    QString tmpPath = path;
    int separatorPos = 0;
    QSet<QString> nonSymlinks;
    QSet<QString> known;

Damit wird realpath nicht mehr aufgerufen und die Qt-Bibliotheken können kompiliert werden. Euer PATH sollte außerdem den Toolchain-Pfad enthalten, bei mir also:

export PATH=/opt/mipsel-linux-uclibc/usr/bin:$PATH

Mein configure-Aufruf für Qt sieht nun so aus:

./configure -embedded mips -xplatform qws/linux-mipsel-g++ -little-endian --prefix=/usr/local \
   -no-javascript-jit -no-webkit -no-script -no-scripttools -no-declarative -qt-kbd-linuxinput

Die Javascript-Teile von Qt können mit der Toolchain aus irgendeinem Grund nicht kompiliert werden, deswegen schließe ich sie einfach aus. Die letzte Option -qt-kbd-linuxinput kompiliert den USB-Eingabe-Treiber von Qt, so dass später die Buttons als Eingabe benutzt werden können. Standardmäßig würde nur der TTY-Treiber eingebunden, dieser macht aber auf dem Dingoo Probleme. Nach erfolgreichen configure ruft ihr noch make auf, und nach geduldigem Warten findet ihr die gebauten MIPS-Qt-Bibliotheken im Pfad qt-everywhere-opensource-src-4.6.2/lib.

Die Bibliotheken könnt ihr nun einfach auf die Dingux-Speicherkarte kopieren. Dort findet ihr ein Verzeichnis local/lib, da müssen alle .so-Dateien sowie die Ordner fonts und pkgconfig hinein. Beachtet, dass die Speicherkarte VFAT-formatiert ist, die Symlinks in qt-everywhere-opensource-src-4.6.2/lib könnt ihr nicht einfach kopieren. Ich kopiere bei mir einfach alle .so.4.6.2-Dateien und kopiere dann jede einzelne zweimal und bennene die Endungen jeweils um nach .so.4 und .so. Mein Verzeichnis auf der Speicherkarte sieht dann so aus:

Wer sich das Kompilieren sparen will kann einfach folgendes Paket herunterladen und den Inhalt des entpackten Verzeichnisses auf seine Speicherkarte nach local/lib kopieren:

Vorkompilierte Qt-Bibliotheken für den Dingoo A320

Ein Qt-Spiel auf dem Dingoo: Asteroids

Das Qt-Everywhere-Paket enthält neben den Qt-Bibliotheken auch einige Beispielanwendungen, um die Qt-Funktionen zu demonstrieren. Eine davon habe ich mir ausgesucht, um sie beispielhaft auf dem Dingoo A320 zum Laufen zu bringen. Ich habe mich für das Spiel Asteroids entschieden, schließlich entwickeln wir hier ja für eine Spielekonsole. Finden könnt ihr die Originalversion des Spiels im Ordner qt-everywhere-opensource-src-4.6.2/examples/graphicsview/portedasteroids. Ich werde hier nur die wichtigsten Anpassungen beschreiben.

Quellcode der Anwendung

Bildschirmauflösung und Fullscreen-Modus

Das Asteroid-Demo wird standardmäßig in einem Fenster mit der Größe 640×480 gestartet. Wenn ihr es auf dem Dingoo startet (die kompilierte MIPS-Version liegt nach dem Bau von Qt im o.g. Verzeichnis), dann seht ihr auf dem Dingoo aber hauptsächlich den Rand des Fensters, und noch einen kleinen Ausschnitt des Hintergrundbildes. Als erstes muss das Spiel also an die Bidlschirmauflösung von 320×240 angepasst werden, außerdem wollen wir das Spiel Fullscreen, also ohne Fenster starten.

Für die Bildschirmauflösung an sich genügen zwei kleine Änderungen in toplevel.cpp sowie view.cpp (Zeile 127 bzw. 117). Außerdem macht es Sinn, alle Sprites nur in halber Größe zu laden, damit sie nicht zu groß auf dem Bildschirm erscheinen. Der Missile-Sprite dürfen wir dabei nicht verkleinern, da sonst die Kollisionserkennung nicht mehr funktioniert. Das Sprite hätte dann nur noch die Größe von einem Pixel. Die Änderungen nehmen wir in view.cpp in der Funktion KAsteroidsView::readSprites() vor, hier nur ein kleiner Ausschnitt (Zeile 317 bis 322):

foreach (QString entry, QDir(fi.path(), fi.fileName()).entryList()) {
    QPixmap p(fi.path() + "/" + entry);
    if (kas_animations[i].id != ID_MISSILE)
        p = p.scaled(p.width() / 2, p.height() / 2, Qt::KeepAspectRatio);
    anim << p;
}

Die Funktion scaled skaliert alle Sprites bis auf jenes mit der ID ID_MISSILE. Zusätzlich sind über die view.cpp alle Werte zur Positionierung anzupassen, z.B. die Position des Abgasstrahl des Raumschiffs, die Positionierung nach dem Teleport, usw.

Für den Fullscreen-Modus genügen dann zwei Zeilen am Ende des Konstruktors KAstTopLevel::KAstTopLevel( QWidget *parent, const char *name ) in toplevel.cpp (Zeilen 303 und 304):

showFullScreen();
qApp->setOverrideCursor( QCursor( Qt::BlankCursor ) );

Damit wird das Widget in den Fullscreen-Modus versetzt und der Mauszeiger versteckt. Das waren dann auch schon die wchtigsten Anpassungen für die Bildschirmauflösung des Dingoo.

Eingabe mit Dingoo-Buttons

Die Dingoo-Buttons werden erfreulicherweise als USB-Eingabegerät von Qt Embedded unterstützt. Man bekommt also ganz normale @keyPressEvent@s und @keyReleaseEvent@s, man kann die entsprechenden Methoden einfach überschreiben. In unserem Fall müssen wir lediglich die Keycodes anpassen, das Asteroid-Demo definiert diese in einer QMap namens actions in der Datei toplevel.cpp (Zeilen 290-300):

    actions.insert( Qt::Key_Up, Thrust );
    actions.insert( Qt::Key_Left, RotateLeft );
    actions.insert( Qt::Key_Right, RotateRight );
    actions.insert( Qt::Key_Space, Shoot );  // X Button

    actions.insert( Qt::Key_Control, Teleport ); // A Button
    actions.insert( Qt::Key_Down, Brake );
    actions.insert( Qt::Key_Alt, Shield ); // B Button
    actions.insert( Qt::Key_Return, NewGame ); // Start Button
    actions.insert( Qt::Key_Shift, Launch );  // Y Button
    actions.insert( Qt::Key_Escape, Quit ); // Select button

Der Vollständigkeit halber die entsprechenden Codes für die Schultertasten des Geräts: Qt::Key_Backspace wäre rechte Schultertaste, Qt::Key_Tab die linke Schultertaste. Zu beachten ist außerdem, dass die Buttons alle Auto-Repeat-Tasten sind, wir müssen also in der Demo-Anwendung die entsprechende if-Anweisungen der Funktionen KAstTopLevel::keyPressEvent( QKeyEvent *event ) und void KAstTopLevel::keyReleaseEvent( QKeyEvent *event ) anpassen (Zeilen 318 und 365). Dort wurden ursprünglich alle Auto-Repeat-Key-Events ignoriert, diese müssen wir wieder aufnehmen.

Damit können nun auch die Buttons des Dingoo zur Steuerung unseres Asteroid-Spiels verwendet werden.

Installation des Spiels auf Dingux

Anwendungen unter Dingux installiert man am Besten auf die Speicherkarte in das Verzeichnis local/apps. Für unser Spiel legen wir dort ein Verzeichnis asteroids an, in das wir das kompilierte Programm portedasteroids und eines der Sprites als Anwendungsicons in einen Unterordner icon (ich verwende das Sprite sprites/rock1/rock10006.png). Alle anderen Grafiken sind in das Programm per Ressourcendatei hineinkompiliert. Bei mir auf der Speicherkarte sind das dann so aus:

Das Dingux-Menü wird bei den beiden Distributionen, die ich kenne, über eine Konfigurationsdatei dmenu.cfg definiert. Diese Datei befindet sich auf der Speicherkarte im Ordner local/dmenu. Dort könnt ihr nun eines der Menüs um folgende Eintrag erweitern:

MenuItem Asteroids
{
Icon = "/usr/local/apps/asteroids/icon/asteroids.png"
Name = "Asteroids"
Executable = "LD_LIBRARY_PATH=/usr/local/lib QWS_KEYBOARD=\"USB:/dev/event0\" ./portedasteroids -qws"
WorkDir = "/usr/local/apps/asteroids"
}

Der Anwendung werden also die beiden Umgebungsvariablen LD_LIBRARY_PATH und QWS_KEYBOARD übergeben. Die Option -qws sagt dem Programm, dass es keinen zentralen Qt Windowing System-Prozess gibt. Dieser ersetzt unter einigen Qt Embedded-Installationen den X Server, wir haben aber auf dem Dingoo keinen solchen Dienst laufen. Wenn ihr die dmenu.cfg so auf die Speicherkarte speichert und das System neu bootet sollte dann das entsprechende Icon im Dingux-Menü erscheinen:

Mit dem Button A startet ihr das Spiel:

Der Vollständigkeit halber hier die Erklärung zur Steuerung

  • Steuerkreuz hoch und runter: Beschleunigen und Bremsen (fürs Bremsen müsst ihr erst Addons sammeln, diese erscheinen wenn ihr bestimmte Asteroiden zerschießt)
  • Steuerkreuz links und rechts: Schiff drehen
  • X: Schießen
  • A: Teleport (per Addon)
  • B: Schutzschild (per Addon)
  • Y: Neustart nachdem euch ein Asteroid getroffen hat
  • Start: kompletter Restart des Spiels
  • Select: Spiel beenden

Und hier noch zum Download das gesamte Projekt sowie eine Binärversion des Spiels:

Die Weitergabe des Sourcecodes erfolgt hier nach den Bedingungen von Nokia, bitte beachtet diese in den Kommentaren der Quellcode-Dateien.

Viel Spaß damit!

Fazit

Mit dem Dingoo A320 gibt es eine für Homebrew-Spiele-Entwicklungen äußerst interessante Plattform. Das Gerät führt nicht nur die Emulatoren klassischer Spielekonsolen aus, mit Dingux gibt es ein ausgewachsenes Linux für das schon einmal Linux-Spiele portiert werden können. Erfreulich, dass sich die Qt Embedded-Kompilierung relativ unkompliziert gestaltet: so hat man ein für Entwickler sehr interessantes Framework auf der Plattform zur Verfügung. Der nächste aus meiner Sicht logische Schritt wäre nun eine Gluon-Version für den Dingoo A320. Diese auf Qt basierende Spieleentwicklungsbibliothek soll in Zukunft unter dem KDE und Maemo laufen. Der Dingoo wäre da ein interessantes weiteres Gerät für die Krossplattform-Spieleentwicklung.