HowtoQt
This document is not yet complete - stay tuned
Qt and QML on WebOS devices
This document explains how to set-up Qt 4.8 (Trolltech/Nokia's cross-platform application and UI framework) on WebOS devices, and then how to cross-compile Qt or QML apps.
This guide assumes:
- a Linux development environment (here Kubuntu 11.10 running in virtualbox on a win7 host)
- Qt 4.8 (the process for Qt 4.7.x is mostly identical; no idea for Qt 5)
- OpenSSH running on the webos device to scp files to it
- The reasonable development tools
The steps include:
- Getting and setting-up the Palm PDK
- Optionally: completing the PDK with some missing includes
- Getting Qt 4.8, setting it up for cross-compilation
- Cross-compilation of Qt 4.8
- Updated webos port of Qt
- Application tweaks to look for libs in the right places
1. Getting and setting up the Palm PDK
Qt is cross-compiled with the official Palm PDK. The PDK plays nice: it can be installed alongside the webos internals WIDK without conflicts; there is no need to modify paths or environment variables.
Note that I haven't "fully" installed the PDK. In particular I haven't set-up the emulator. The basic stuff needed are: the arm gcc toolchain, and novacom to communicate with the device. VirtualBox can be skipped. Java may be necessary.
- Read about the Palm SDK 3.05 and the installation procedure. Follow the instructions on there. But essentially this boils down to:
- Download novacom from that page
- Download the SDK from that page
- Install novacom and the SDK following the instructions. I.e.:
- sudo dpkg -i palm-sdk_3.0.5-svn528736-pho676_i386.deb
- sudo dpkg -i palm-novacom_1.0.80_i386.deb
- The SDK is now in /opt/PalmPDK
You may check that all is fine so far (or skip this - this is simply to step-by-step see if all is ok):
- You can invoke gcc or g++ from the PDK arm toolchain to compile some test program. E.g. /opt/PalmPDK/arm-gcc/bin/arm-none-linux-gnueabi-g++ hello.cpp -o hello
- You can copy this file to the usb drive on the device: scp hello root@ip.address.of.device:/media/internal
- You can go log on the device with novaterm: novaterm
- You can run your test program: /media/internal/hello
If that works then on to the next steps.
2. Optional: completing the PDK with some missing includes
The Palm PDK contains an arm toolchain which can be used as-is to cross-compile Qt. This step can be skipped, unless you want maximum performance with JavaScript in WebKit.
There is one issue with the Palm PDK. The Qt configuration script relies on the existence of one include file - /opt/PalmPDK/include/asm/hwcap.h (actually more includes in the /opt/PalmPDK/include/asm directory are needed) - to know whether programs can identify the characteristics of the CPU, such as the availability of Neon instructions.
Without these includes, Qt will cross-compile and work, but the just-in-time JavaScript compiler in WebKit will be deactivated. This decreases JavaScript performance by a factor ~3.
Therefore the recommended approach is to provide the missing include files. These files can be found in the CodeSourcery arm toolchain.
- Download the arm toolchain "Sourcery G++ Lite 2009q3-67 for ARM GNU/Linux" from Mentor Graphics' website. You need to register to download, but it's free. Make sure you get the ARM GNU/Linux Release (not the ARM EABI release!). The file you get should be called arm-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
- Untar this file: tar xvfj arm-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
- Copy the entire include/asm directory to the PalmPDK: cp -r arm-2009q3/arm-none-linux-gnueabi/libc/usr/include/asm /opt/PalmPDK/include
You're set!
3. Getting Qt 4.8, setting it up for cross-compilation
This step gets the Qt 4.8 sources, and sets-up Qt for cross-compilation using the Palm PDK.
3.1 Get the Qt sources
- Cd to your home directory: cd ~
- Get the Qt 4.8 sources: http://get.qt.nokia.com/qt/source/qt-everywhere-opensource-src-4.8.0.tar.gz
- Untar: tar xvfz qt-everywhere-opensource-src-4.8.0.tar.gz
3.2 Qt mkspecs for WebOS
We set-up the mkspecs for WebOS (compiler parameters required for cross-compilation):
- Cd to the qws mkspecs: cd ~/qt-everywhere-opensource-src-4.8.0/mkspecs/qws
- The WebOS mkspecs are mostly the same as the linux-arm-gnueabi-g++ mkspecs. So we copy them and modify them afterwards: cp -r linux-arm-gnueabi-g++ linux-webos
- Edit the linux-webos mkspecs to set-up the cross-compiler path and other build parameters. E.g. vi ~/qt-everywhere-opensource-src-4.8.0/mkspecs/qws/linux-webos/qmake.conf. This file must look like this:
# # qmake configuration for WebOS builds with arm-linux-g++ # #for 4.7.4: #include(../../common/g++.conf) #include(../../common/linux.conf) #include(../../common/qws.conf) # for 4.8 include(../../common/linux.conf) include(../../common/gcc-base-unix.conf) include(../../common/g++-unix.conf) include(../../common/qws.conf) # modifications to g++.conf #Toolchain #Base directory of gcc toolchain GCCBASE = /opt/PalmPDK/arm-gcc/bin TSLIB_INCDIR = /opt/PalmPDK/include TSLIB_LIBDIR = /opt/PalmPDK/device/lib #Compiler Flags to take advantage of the ARM architecture QMAKE_CFLAGS_RELEASE = -O2 -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp -ftree-vectorize QMAKE_CFLAGS_DEBUG = -O0 -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp QMAKE_CXXFLAGS_RELEASE = -O2 -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp -ftree-vectorize QMAKE_CXXFLAGS_DEBUG = -O0 -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp # For official pdk QMAKE_CC = $$GCCBASE/arm-none-linux-gnueabi-gcc QMAKE_CXX = $$GCCBASE/arm-none-linux-gnueabi-g++ QMAKE_LINK = $$QMAKE_CXX QMAKE_LINK_SHLIB = $$QMAKE_CXX QMAKE_RANLIB = $$GCCBASE/arm-none-linux-gnueabi-ranlib # modifications to linux.conf QMAKE_AR = $$GCCBASE/arm-none-linux-gnueabi-ar cqs QMAKE_OBJCOPY = $$GCCBASE/arm-none-linux-gnueabi-objcopy QMAKE_STRIP = $$GCCBASE/arm-none-linux-gnueabi-strip QMAKE_INCDIR = $$TSLIB_INCDIR QMAKE_LIBDIR = $$TSLIB_LIBDIR QMAKE_LIBS += -lrt -lz QMAKE_LFLAGS += -Wl,--allow-shlib-undefined load(qt_config)
3.3 gfxdrivers plugin for WebOS
Qt embedded needs a gfxdrivers plugin which allows Qt to show things on a screen, get keyboard input and mouse events. This step gets the WebOS Qt gfxdrivers plugin.
The initial plugin version was written by Darron Black [1,2] and it uses SDL for graphics. With the changes in WebOS 2.x, that plugin doesn't work anymore: it uses root access to read the keyboard, but recent PDK apps are not running as root anymore but in jails. Therefore an extended version is required that handles keyboard input for "non-root" users. That version is available from Qt gfxdrivers plugin for WebOS on Google Code. It has also tentative support for screen rotation.
- Cd to the Qt gfxdrivers directory: cd ~/qt-everywhere-opensource-src-4.8.0/src/plugins/gfxdrivers
- Get the WebOS gfxdrivers plugin from the svn repository: svn checkout http://qt-webos.googlecode.com/svn/trunk/ webos
- Edit ~/qt-everywhere-opensource-src-4.8.0/src/plugins/gfxdrivers/gfxdrivers.pro to add the line: contains(gfx-plugins, webos) :SUBDIRS += webos
- In order to support screen rotation we need to patch the "transformed" screen driver. This driver is provided by Qt to do screen rotation or offsetting and it uses as back-end the driver doing the actual access to the pixels (that would be our webos qgfxdrivers). Unfortunately the transformed driver doesn't allow to "notify" SDL when pixels are changed. A quick hack is to patch ~/qt-everywhere-opensource-src-4.8.0t/src/gui/embedded/qscreentransformed_qws.cpp. In this patch we hijack the functionality of the "blank" method (which is supposed to blank the screen) to notify the WebOS gfxdrivers before and after pixels are modified. So edit qscreentransformed_qws.cpp around lines 430 and add the two lines marked with the comments below:
. . . screen()->blank(true); // ADD THIS FOR WEBOS QWSDisplay::grab(); for (int i = 0; i < rects.size(); ++i) { const QRect r = rects.at(i) & bound; QPoint dst; switch (trans) { case Rot90: dst = mapToDevice(r.topRight(), QSize(w, h)); break; case Rot180: dst = mapToDevice(r.bottomRight(), QSize(w, h)); break; case Rot270: dst = mapToDevice(r.bottomLeft(), QSize(w, h)); break; default: break; } func(this, image, r.translated(-topLeft), dst); } QWSDisplay::ungrab(); screen()->blank(false); // ADD THIS FOR WEBOS . . .
3.4 Cross-compile
Now we can configure and cross-compile Qt. The lines below configure Qt to install in ~/qte-48:
- cd ~/qt-everywhere-opensource-src-4.8.0
- ./configure -v -prefix ~/qte-48 -embedded arm -platform qws/linux-x86-g++ -xplatform qws/linux-webos -depths 16,24,32 -no-multimedia -no-audio-backend -no-phonon -no-phonon-backend -no-nis -no-iconv -no-dbus -no-cups -no-largefile -no-accessibility -no-gtkstyle -no-qt3support -qt-gfx-vnc -plugin-gfx-vnc -no-glib -qtlibinfix 48 -xmlpatterns -exceptions -opensource -make libs -nomake tools -nomake demo -nomake examples -nomake docs -webkit -javascript-jit -script -scripttools -declarative -openssl -qt-gfx-transformed
- make -j4
- make install
NEED TO COMPILE WEBOS manually
- cd
- make
- make install
Hopefully you've made it so far without problems! You now have Qt in ~/qte-48.
4. Compiling Qt apps
We see here how to compile a Qt app for Qt on WebOS. We look at two examples that are directly taken from the Qt distribution:
- fancybrowser: this is a Qt-widget-based example browser. It is available in QTSDKDIR/Examples/4.8/webkit/fancybrowser
- webbrowser: this is a Qt-QML-based example browser. It is available in QTSDKDRIR/Demos/4.8/declarative/webbrowser
The way compilation of the apps is mostly identical, but the QML-based project has a few extra complexities so we'll see first the widget-based project.
4.1 Qt-widget app
We assume a copy of QTSDKDIR/Examples/4.8/webkit/fancybrowser in your working directory, e.g. ~/fancybrowser
Two things must be modified in the project:
- fancybrowser.pro: this is the project file, it must be modified to include the proper libraries
- main.cpp: this is the file in which the QApplication object is instanciated. We must do some setup to indicate to the application:
- where to find system fonts,
- to start the application as a GUI server application
- to use the WebOS gfxdrivers plugin, or to use the transformed gfxdrivers plugin in combination with the WebOS gfxdrivers plugin to support screen rotation.
Note that one could avoid modifying main.cpp altogether because the same results can be achieved by environment variables and by command line arguments. Therefore a script could set the environment variables and call the executable with the appropriate parameters. However we go through the self-contained solution, which is a bit more complex but may seen as more clean.
Modifications to the .pro file
The pro file must be modified to link the PDL and SDL libs, and also to look for the Qt libraries at the right location at runtime. Here we assume that the Qt libs are in a subdirectory called 'qt' below the directory where the application is deployed.
Modify fancybrowser.pro and add this at the end:
!win32: { message(assume qws) # Set the runtime path to a relative directory w.r.t. the location of the executable LIBS += -Wl,-rpath,"'\$$ORIGIN/qt'" -L/opt/PalmPDK/device/lib -lpdl -lSDL INCLUDEPATH += /opt/PalmPDK/include/SDL }
Modifications to the main file
QApplication::GuiServer
- The first thing
int main(int argc, char * argv[]) { int rv; #ifdef Q_WS_QWS #warning "Hacks for QWS" // ------------------------------------------------------------------------ // FONT SETUP FONT SETUP FONT SETUP FONT SETUP FONT SETUP // Get the path to the exe... // Ideally we should use QApplication::applicationDirPath, but instanciating that // one attempts to load the fonts... which aren't defined yet char pathexe[1024]; rv = readlink("/proc/self/exe", pathexe, sizeof(pathexe)); if(!(rv>0 && rv<sizeof(pathexe))) { printf("Couldn't get the exe path - terminating\n"); return -1; } pathexe[rv]=0; printf("path to exe: %s\n",pathexe); *strrchr(pathexe,'/')=0; // Eliminate the application name QString fontpath = QString(pathexe)+"/fonts"; printf("font path: %s\n",fontpath.toStdString().c_str()); //if(!qputenv("QT_QWS_FONTDIR",fontpath.toAscii())) if(!qputenv("QT_QWS_FONTDIR","/usr/share/fonts")) { printf("Can't set font path"); } // // FONT SETUP FONT SETUP FONT SETUP FONT SETUP FONT SETUP // ------------------------------------------------------------------------ // Hack setting the the display driver int argc2 = argc+2; char **argv2 = new char*[argc2]; for(int i=0;i<argc;i++) { argv2[i] = new char[strlen(argv[i])+1]; strcpy(argv2[i],argv[i]); } //char *display[]={"-display","PalmPreSDLFb:100"}; char *display[]={"-display","Transformed:PalmPreSDLFb:1"}; argv2[argc] = display[0]; argv2[argc+1] = display[1]; for(unsigned i=0;i<argc2;i++) { printf("%d: %s\n",i,argv2[i]); } // QApplication::GuiServer is akin to passing -qws on the command line // Passes argv2 / argc2 which stays alive for the entire duration of QApplication QApplication app(argc2, argv2,QApplication::GuiServer); //QApplication app(argc, argv); //QCustomApplication app(argc, argv); //QCustomApplication app(argc2, argv2,QApplication::GuiServer); #else #warning "No QWS, all OK" QApplication app(argc, argv,QApplication::GuiServer); #endif QUrl url; if (argc > 1) url = QUrl(argv[1]); else //url = QUrl("http://www.google.com/ncr"); url = QUrl("http://www.slashdot.org"); MainWindow *browser = new MainWindow(url); #if defined Q_OS_SYMBIAN || defined Q_WS_HILDON || defined Q_WS_MAEMO_5 || defined Q_WS_SIMULATOR || defined Q_WS_QWS browser->showMaximized(); #else browser->show(); #endif /* const int width = 640; const int height = 480; QGraphicsScene scene; QGraphicsView view(&scene); view.setFrameShape(QFrame::NoFrame); view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QGraphicsWebView webview; webview.resize(width, height); webview.load(QUrl("http://www.slashdot.org")); scene.addItem(&webview); view.resize(width, height); view.show();*/ rv = app.exec(); return rv; }
4.2 Qt-QML app
4.x deployment
4.x stuff
- numbering of the qws stuff to avoid conflicts
5. deploy
A. Links
[1] Qt on the Palm Pre [2] Qt port to webOS by Darron Black on gitorious