Adding Windows 7/8 Multitouch capabilities to a libGDX game

pingK originally was a PC game created in 2006 by some friends and me over the course of 2 days. It subsequently won the Graphics meets Games award at the Eurographics 2006 conference in Vienna, and was also featured in the pong.mythos exhibition of the Computer Games museum in Berlin. I created the Android version in 2013 from scratch, using libGDX.


libGDX is a great library/middleware that allows you to develop games for multiple platforms from the same Java source, which sounds like not such a big deal if you’re coming from a C++ background, but trust me, it’s not so easy on Java which is kind of surprising (mostly because Java has no preprocessor support, so you cannot #ifdef yourself around some stuff). It supports creating Android, Desktop (Win/Mac/Linux), and even iOS and GWT/HTML5 apps from the same source code, with an API that allows you to access common features such as input and OpenGL graphics in the same way on all platforms.

Originally, I used libGDX as an OpenGL binding for Android during Android 2.2 (Frozen Yoghurt, or FroYo) years, because I wanted to target Android 2.2 devices (still the most common back then), however Google seemed to have forgotten to add Java bindings for OpenGL 2.0 glDrawElements() and glVertexAttribPointer(), which are some crucial functions. You had to either use the NDK and C++ to use OpenGL 2.0 properly, or create your own Java bindings via JNI.

libGDX did exactly that, and so I originally used it for just that (still do, I don’t use any of the higher level graphics stuff such as model loading or camera handling that libGDX provides).

Now, having also a desktop build for development and debugging, I was thinking that it would be nice to have a working Windows build with multitouch (which has been in Windows since Windows 7). Some of those larger Windows tablets, such as the Samsung Series 7 slate I had back then, or the Surface tablets, or the gadzillion Windows 8 tablets that seem to be popping up recently, would be nice devices to actually play pingK in “hotseat” 2-player mode. That requires multitouch support though, which libGDX does not offer for desktop builds. So I had to add that, and here’s – after a long introduction – how I did it.

JNI Magic

The first step was to interface the Windows API with Java. For that, you essentially use JNI (Java Native Interface). I won’t explain in detail here how to do that as there are plenty of resources on the web that explain exactly how to set up a simple JNI DLL.

What I am focusing on is how to hook Windows WM_TOUCH messages in a way that they can be processed from within the libGDX app.

So, first I have a JNI exported method inside the DLL like this

// JNI exported method
JNIEXPORT void JNICALL Java_com_gebauz_Bauzoid_platform_windows_Win7TouchInput_initTouch(
    JNIEnv* env, jobject jthis, jlong jhwnd)
    // do C++ stuff here

The Java interface to call this native function looks like this:

public native void initTouch(long hwnd);

It can then be used like any other Java method:

long hwnd = 0;
Object displayImpl = null;
Method[] displayMethods = Display.class.getDeclaredMethods();
for (Method m : displayMethods)
    if (m.getName().equals("getImplementation"))
        displayImpl = m.invoke(null, (Object[])null);
Field[] windowsDisplayFields = displayImpl.getClass().getDeclaredFields();
for (Field f : windowsDisplayFields)
    if (f.getName().equals("hwnd"))
        hwnd = (Long) f.get(displayImpl);
initTouch(hwnd);    // Call into JNI DLL function!

Essentially I do some reflection magic on LWJGL’s Display implementation (libGDX uses LWJGL for its desktop OpenGL handling) to retrieve the Windows HWND, and send that to the DLL via initTouch(). Strictly speaking I am pretty much violating the code by making private members accessible, but all other methods seemed not robust enough (like getting the HWND via window title string).

So now that the DLL has the HWND, it can simply call

originalWndProc = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (LONG)WindowSubClassProc);
RegisterTouchWindow(hwnd, TWF_WANTPALM);

to first subclass the window (i.e. register an alternative window procedure), and finally, to register the main game window as “multitouch-handling” so that WM_TOUCH messages will be sent to this window.

Now in WindowSubClassProc, the actual magic happens:

LRESULT APIENTRY WindowSubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    switch (uMsg)
        case WM_TOUCH:
        // handle touch messages
    return CallWindowProc(wpOrigEditProc, hwnd, uMsg, wParam, lParam);

Sending data back to Java

In WM_TOUCH, I simply call into Java again by using the JNIEnv pointer that I initially got when calling initTouch inside the DLL (I just store it somewhere in a variable for later use). I construct a WindowsTouchInfo Java class instance in C/C++ (error handling not shown for brevity):

// find the class
jclass touchInfoClass = jniEnv->FindClass(
// get constructor (parameter-less)
jmethodID methodConstructor = jniEnv->GetMethodID(touchInfoClass, "", "()V");
// invoke constructor
jobject touchInfoInstance = jniEnv->NewObject(touchInfoClass, methodConstructor);
// get field WindowsTouchInfo.x which is an integer ("I") and set it
jfieldID fieldX = jniEnv->GetFieldID(touchInfoClass, "x", "I");
jniEnv->SetIntField(touchInfoInstance, fieldX, touchPosX);

This WindowsTouchInfo instance gets the information from the touch event, and gets passed to another Java function from C++. On my Java framework side, I have an input wrapper between the actual game code and libGDX that accepts these WindowsTouchInfo instances and processes them.

This is just a quick summary of the steps necessary to get multitouch working for libGDX desktop builds. Note that the result of this is a Windows 7/8 desktop build (that also runs on older Windows versions, it just doesn’t support multitouch there), not a Windows Store app. At the time of writing, libGDX does not support creating Windows Store apps (or Windows Phone 8 apps) yet.

Leave a Reply

Your email address will not be published. Required fields are marked *