Qt Webkit and Javascript magnetic stripe example
Published on March 1, 2011
At Boundary Devices, we're relative newcomers to the world of Qt, but we were somewhat surprised to learn about Nokia's decision to favor Windows CE instead of MeeGo for future smart phone designs. It's hard for us to say what the right direction may be for the cell-phone marketplace; nevertheless, we're very impressed with the code quality of Qt and the power of Qt Webkit. We've been looking at it with an eye toward how it may be used to integrate hardware like barcode scanners, RFID readers, and magnetic stripe readers. In this post, we'll show how we used the Qt Webkit Bridge to allow Ryan's magstripe driver to deliver events and data directly to a browser.
What events does it handle?
As described in Ryan's earlier post, we've connected a Neuron insertion-style reader to the GPIO ports of the Nitrogen i.MX51 board. These readers are commonly used in casino player-tracking applications to determine when a customer is present and playing a particular slot machine so their loyalty account will be credited with their play. This reader has two switches, front and rear, that allow an application to detect when a card has been inserted and whether it's still present. So, in addition to the information encoded on the magnetic stripe, we have events for each of the switches going open and closed. In the mag_decode driver, all events are delivered in text lines from/dev/magstripe
. Switch transitions are indicated by lower and UPPER-case letters F
and R
for the front and rear switches respectively. Any line containing more than a single letter is data from the magnetic stripe itself.
F
- Front switch openf
- Front switch closedR
- Rear switch openr
- Rear switch closeddata
- Magstripe data
How are they handled?
We deliver the events up to Javascript in the form of two callbacks,switchChange()
and swipe()
:
magstripe.switchChange.connect(function(s){ console.log("switchChange: "+s); }); magstripe.swipe.connect(function(s){ console.log("swipe: "+s); });Note that the
magstripe
global is the object defined by the Webkit bridge. We'll describe the C++ code in detail below. In Qt parlance, the switchChange()
and swipe()
handlers above are slots connected up to the signals generated by C++.
There are other ways to approach this integration. You could use a keyboard wedge and have your app keep track of key-down
and key-up
events, but you'd need a lot of state and you'd have to be careful about the input focus. You'd also be stuck wondering whether a card is present during initialization.
That last problem is dealt with by the next two Javascript interfaces, getMagstripe()
and getSwitchState()
:
var swstate = magstripe.switchState ; console.log( "switch state: "+swstate+"n"); var ms = magstripe.magstripe ; console.log("mag stripe: "+ms);These two methods of the
magstripe
allow your web pages to query the state of the mag stripe reader at initialization time. If you were going the keyboard-wedge route, you'd need to pass that state between pages yourself.
How do I get my app to read these events?
As we mentioned before, the key to this whole scheme is the Qt Webkit Bridge. This technology builds upon the solid footing of Qt, allowingQObjects
to be exposed to Qt Webkit through the QWebFrame::addToJavaScriptWindowObject()
method. This method allows you fine-grained control over what is exposed to the web environment, and includes support for data access and signal delivery.
The Qt docs do a great job of describing the Webkit bridge, so we won't delve too far into the details. Instead, we'll describe the primary interfaces we used to implement the magstripe
object. The sources for this are available for download.
The following is a slightly stripped version of the declaration of class magstripe_t
.
class magstripe_t : public QObject { Q_OBJECT public: Q_PROPERTY(QString magstripe READ getMagstripe); QString getMagstripe(void); Q_PROPERTY(QString switchState READ getSwitchState); QString getSwitchState(void); Q_INVOKABLE void clear(); signals: void switchChange(QString); void swipe(QString); private slots: void readData(int fd); ... };Things to note about this declaration:
magstripe_t
is aQObject
. This is requirement #1.magstripe_t
makes two properties available through theQ_PROPERTY
macro. This is really all that's necessary to provide themagstripe
andswitchState
interfaces. Also note that these properties are implemented using getter methodsgetMagstripe
andgetSwitchState
.magstripe_t
exposes aclear()
method that is callable from Javascript through theQ_INVOKABLE
macro. I forgot to mention this before.magstripe_t
can emit two signals:switchChange
andswipe
. Handlers for these aren't even implemented in C++, as they're designed to be handled in web pages.magstripe_t
contains a single slot:readData()
. The implementation of this module makes use of something calledQSocketNotifier
which allows a file descriptor (to/dev/magstripe
in this case) to be polled by the main event loop. When data is available to be read from the file descriptor, the main loop generates a signal which is handled by thereadData
slot. If you read the implementation of this method inmagstripe.cpp
, you'll see that the code simply parses lines coming from/dev/magstripe
and emits either theswitchChanged()
orswipe()
signals.