Before ending up with the current design, a few studies have been made:
- Reading raw keyboard events from the PC, using adb and getevent.
- Generating multitouch events (*) from a small program written in C.
- Doing the above from Java (requiring only rooting, but no daemon). This proved slightly too slow and introduced a noticeable lag, so I opted for a daemon written in C, and configured by a Java app.
(*) see http://www.mjmwired.net/kernel/Documentation/input/multi-touch-protocol.txt Architecture
The final architecture is based on a daemon, that is, a small standalone program (written in C and hence very fast) communicating with a Java service.
The diagram in this step is an UML class diagram, showing the principal classes and entities, and their relations. pinball_buttons_mapper
(aka the daemon) - this is the standalone C program, actually doing all the "real" job: it waits for keyboard events, and generates multitouch events (simulating finger touches on the screen). It reads all its parameters from the compact settings file. The daemon is started by the Java app as root, because it needs such permissions to read/write events directly from /dev/input/eventN
device drivers from the underlying Linux OS. The demon is not able to determine the devices to use, screen size and orientation, etc, so it reads these parameters from the settings.cnf
file, that was prepared by the Java part. The daemon is able to detect that the settings file has changed and to re-read it automatically. SettingsActivity
- this is the main screen that is shown when starting the program. It lets you set all parameters, and stores them as preferences. It also (re)starts the service, and calls the CompactSettings upon settings changes. CompactSettings
(aka settings.cnf) - writes a copy of the settings, in a very compact format that the daemon can easily read. The settings are in XML, but the compact copy is very much simpler. TheService
- this is Java code running in background, even when no screen of the app is visible. It makes an icon always visible in the Android notification area, and cares for (re) starting the daemon (its role is merely to monitor the daemon, and call the settings screen). Mapper
- this is a Java class designed to install the daemon, and (re)start it. BroadcastReceiver
- this is needed to make the app automatically start when the tablet boots. Logs
- provides a screen showing debug logs, useful when developing the software. UncaughtExceptionHandler
- normally, when a Java apps encounters a bug leading to a creash, it stops. This handler instead displays some technical information (the stack trace) to help locating the problem without a debugger attached via a PC. Compiling and building
The Java part was developed under Eclipse. The daemon is cross-compiled, and stored into the Java app as a raw resource.
To build all, run the build_all.sh
script. Its final product is the APK file in bin/
. The build script works under Linux and Mac OSX, and requires the Android SDK to be installed, as well as the C cross-compiler (arm-linux-gnueabi-gcc
). To develop the Java part, and if build_all.sh
was run once before in order to compile the C daemon, everything can be done from within Eclipse as usual.
The archive for source code can be found in step 10.