Index: zaz-0.2.9+dfsg1/configure.ac
===================================================================
--- zaz-0.2.9+dfsg1.orig/configure.ac	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/configure.ac	2010-10-03 21:06:51.000000000 +0200
@@ -18,6 +18,7 @@
 AC_CHECK_LIB(GLU, gluLookAt, [], AC_MSG_ERROR(no libGLU found))
 AC_CHECK_LIB(SDL_image, IMG_Load, [], AC_MSG_ERROR(no SDL_image found))
 AC_CHECK_LIB(vorbisenc, vorbis_encode_init, [], AC_MSG_ERROR(no vorbisenc found))
+AC_CHECK_LIB(Xi, XISelectEvents, [], AC_MSG_ERROR(no XI2 found))
 
 AC_CHECK_HEADER(GL/glu.h, [], AC_MSG_ERROR(no GL/glu.h found))
 AC_CHECK_HEADER(SDL/SDL_image.h, [], AC_MSG_ERROR(no SDL/SDL_image.h found))
Index: zaz-0.2.9+dfsg1/src/ballpath.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/ballpath.cpp	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/ballpath.cpp	2010-10-03 21:06:51.000000000 +0200
@@ -119,6 +119,9 @@
 
 void BallPath::Logic()
 {
+    // Reset pending accuracyShots 
+    state.accuracyShotTriggered = false;
+
     if (extraBallFading)
     {
         if (!balls.empty())
Index: zaz-0.2.9+dfsg1/src/editor.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/editor.cpp	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/editor.cpp	2010-10-03 21:06:51.000000000 +0200
@@ -367,10 +367,10 @@
 
     if (!events.empty)
     {
-        mx = (vwidth * events.mouseX) + vleft;
-        my = vheight - (vheight * events.mouseY);
+        mx = (vwidth * events.mouseX[0]) + vleft;
+        my = vheight - (vheight * events.mouseY[0]);
 
-        if (events.buttDown[0])
+        if (events.buttDown[0][0])
         {
             if ((grab == false) && (hoverPoint != -1))
             {
@@ -419,8 +419,7 @@
                     Game game(settings, surface, level, 0, 0, 0, 0, true);
                     game.Run();
                     GLSetup();
-                    SDL_ShowCursor(SDL_ENABLE);
-                    SDL_WM_GrabInput(SDL_GRAB_OFF);
+		    GrabInput(0);
                     return;
                 }
 
@@ -429,8 +428,7 @@
                     Game game(settings, surface, level, 0, (uint)time(0), 5, 0, false);
                     game.Run();
                     GLSetup();
-                    SDL_ShowCursor(SDL_ENABLE);
-                    SDL_WM_GrabInput(SDL_GRAB_OFF);
+		    GrabInput(0);
                     return;
                 }
 
Index: zaz-0.2.9+dfsg1/src/frame_events.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/frame_events.cpp	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/frame_events.cpp	2010-10-03 21:06:51.000000000 +0200
@@ -20,20 +20,23 @@
 
 namespace Scenes
 {
-    FrameEvents::FrameEvents(vector<SDLKey>kup, vector<SDLKey>kdn, double mx, double my, int relmx, int relmy, bool bup[], bool bdn[])
+    FrameEvents::FrameEvents(vector<SDLKey>kup, vector<SDLKey>kdn, double mx[], double my[], int relmx[], int relmy[], bool **bup, bool **bdn)
     {
 		unicode.clear();
 		keyUp = kup;
         keyDown = kdn;
-        mouseX = mx;
-        mouseY = my;
-        relmouseX = relmx;
-        relmouseY = relmy;
-
-        for (int i = 0; i < 3; i++)
-        {
-            buttUp[i] = bup[i];
-            buttDown[i] = bdn[i];
+
+        for (int i = 0; i < MAX_POINTERS ; i++) {
+            mouseX[i] = mx[i];
+            mouseY[i] = my[i];
+            relmouseX[i] = relmx[i];
+            relmouseY[i] = relmy[i];
+
+            for (int j = 0; j < 3; j++)
+            {
+                buttUp[i][j] = bup[i][j];
+                buttDown[i][j] = bdn[i][j];
+            }
         }
 
         empty = false;
@@ -42,9 +45,12 @@
     FrameEvents::FrameEvents()
     {
         empty = true;
-        for (int i = 0; i < 3; i++)
-        {
-            buttUp[i] = buttDown[i] = false;
+        for (int i = 0; i < MAX_POINTERS ; i++) {
+            for (int j = 0; j < 3; j++)
+            {
+                buttUp[i][j] = false;
+                buttDown[i][j] = false;
+            }
         }
     }
 
Index: zaz-0.2.9+dfsg1/src/frame_events.h
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/frame_events.h	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/frame_events.h	2010-10-03 21:06:51.000000000 +0200
@@ -22,22 +22,24 @@
 #include "common.h"
 #include <vector>
 
+#define MAX_POINTERS 2
+
 namespace Scenes
 {
 
     class FrameEvents
     {
         public:
-            FrameEvents(vector<SDLKey>kup, vector<SDLKey>kdn, double mx, double my, int relmx, int relmy, bool bup[], bool bdn[]);
+            FrameEvents(vector<SDLKey>kup, vector<SDLKey>kdn, double mx[], double my[], int relmx[], int relmy[], bool **bup, bool **bdn);
             FrameEvents();
 
 			vector<Uint16>unicode;
             vector<SDLKey>keyUp;
             vector<SDLKey>keyDown;
-            double mouseX, mouseY;
-            int relmouseX, relmouseY;
-            bool buttUp[3];
-            bool buttDown[3];
+            double mouseX[MAX_POINTERS], mouseY[MAX_POINTERS];
+            int relmouseX[MAX_POINTERS], relmouseY[MAX_POINTERS];
+            bool buttUp[MAX_POINTERS][3];
+            bool buttDown[MAX_POINTERS][3];
             bool empty;
     };
 
Index: zaz-0.2.9+dfsg1/src/game.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/game.cpp	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/game.cpp	2010-10-03 21:06:51.000000000 +0200
@@ -119,7 +119,14 @@
     ballPaths[nPaths] = 0;
 
     // create the player
-    pl = new Player(level.paths[0], ballPaths, level.loop, level.invert);
+    for (int i = 0; i < xi_devices; i++){
+    	if (xi_device_ids[i]) {
+            pl[i] = new Player(i,level.paths[0], ballPaths, level.loop, level.invert);
+        } else {
+            pl[i] = NULL;
+        }
+    }
+
     dying = false;
 
     sfxVol = atoi(settings->get("sfxVolume", "50").c_str());
@@ -168,8 +175,10 @@
     delete ballPaths;
     if (ownTextures)
         glDeleteTextures(14, ballText);
-
-    delete pl;
+ 
+    for (int i = 0; i< xi_devices; i++) {
+        delete pl[i];
+    }
 }
 
 void Game::Logic(ulong frame)
@@ -253,7 +262,9 @@
             }
     }
 
-    pl->Logic(events);
+    for (int i = 0; i< xi_devices; i++) {
+        pl[i]->Logic(events);
+    }
 
     if (editor)
     {
@@ -311,8 +322,10 @@
         ballPaths[p]->state.score = 0;
     }
 
-    score += pl->score;
-    pl->score = 0;
+    for (int i = 0; i< xi_devices; i++) {
+        score += pl[i]->score;
+        pl[i]->score = 0;
+    }
 
     if (score < 0)
         score = 0;
@@ -349,8 +362,10 @@
             finishedDrawing = false;
     }
 
-    if (pl->shooting || pl->picking)
-        finishedDrawing = false;
+    for (int i = 0; i< xi_devices; i++) {
+        if (pl[i]->shooting || pl[i]->picking)
+            finishedDrawing = false;
+    }
 
     if (!dying)
         if (finishedDrawing)
@@ -507,7 +522,10 @@
     {
         ballPaths[b]->Render();
     }
-    pl->Render();
+
+    for (int i = 0; i< xi_devices; i++) {
+        pl[i]->Render();
+    }
 
     glLoadIdentity();
     // render score
@@ -531,7 +549,7 @@
         {
             glPushMatrix();
             glEnable(GL_TEXTURE_2D);
-            glBindTexture(GL_TEXTURE_2D, pl->tex);
+            glBindTexture(GL_TEXTURE_2D, pl[0]->tex);
             glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
             glTranslated(5 + (l * 5) + vleft, 95, 50);
             glScaled(6.0, 6.0, 6.0);
@@ -646,8 +664,7 @@
 {
     if ((mode == RUN) || (mode == RECORD))
     {
-        SDL_ShowCursor(SDL_DISABLE);
-        SDL_WM_GrabInput(SDL_GRAB_ON);
+    	GrabInput(1);
     }
 
     int width = surface->w;
Index: zaz-0.2.9+dfsg1/src/game.h
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/game.h	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/game.h	2010-10-03 21:06:51.000000000 +0200
@@ -60,7 +60,7 @@
         SDL_Surface *surface;
         void CleanupSounds();
 
-        Player *pl;
+        Player *pl[MAX_POINTERS];
         BallPath **ballPaths;
         int nPaths;
 
Index: zaz-0.2.9+dfsg1/src/gameloop.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/gameloop.cpp	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/gameloop.cpp	2010-10-03 21:06:51.000000000 +0200
@@ -227,8 +227,7 @@
 
 void GameLoop::GLSetup()
 {
-    SDL_ShowCursor(SDL_ENABLE);
-    SDL_WM_GrabInput(SDL_GRAB_OFF);
+    GrabInput(0);
 
     int width = surface->w;
     int height = surface->h;
@@ -302,11 +301,11 @@
                     }
             }
 
-        double mx = (vwidth * events.mouseX) + vleft;
-        double my = vheight - (vheight * events.mouseY);
+        double mx = (vwidth * events.mouseX[0]) + vleft;
+        double my = vheight - (vheight * events.mouseY[0]);
         bool click = false;
 
-        if (events.buttDown[0])
+        if (events.buttDown[0][0])
             click = true;
 
         if (showLifeLostMenu)
Index: zaz-0.2.9+dfsg1/src/player.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/player.cpp	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/player.cpp	2010-10-03 21:06:51.000000000 +0200
@@ -20,8 +20,8 @@
 #include "game.h"
 #include <deque>
 
-Player::Player(Bezier path, BallPath **ballPaths, bool pathLooped, bool invert, bool drawPath)
-        : tex(LoadTexture("player.png")), path(path), drawPath(drawPath), pathLooped(pathLooped), invert(invert),
+Player::Player(int id, Bezier path, BallPath **ballPaths, bool pathLooped, bool invert, bool drawPath)
+        : playerId(id), tex(LoadTexture("player.png")), path(path), drawPath(drawPath), pathLooped(pathLooped), invert(invert),
         ballPaths(ballPaths),  pickedCol(-1), pickedBonus(BONUS_NONE), picking(false), shooting(false), score(0),
         accuracyShot(false)
 {
@@ -114,7 +114,6 @@
         {
             accuracyShot = true;
             accuracyShotTime = accuracyShotTimeout;
-            ballPaths[p]->state.accuracyShotTriggered = false;
         }
 
         p++;
@@ -213,12 +212,12 @@
     {
         if (!invert)
         {
-            Move(double(events.relmouseX) * mouseDiv);
+            Move(double(events.relmouseX[playerId]) * mouseDiv);
         }
         else
-            Move(double(-1 * events.relmouseX) * mouseDiv);
+            Move(double(-1 * events.relmouseX[playerId]) * mouseDiv);
 
-        if (events.buttDown[0] && picking && !shooting)
+        if (events.buttDown[playerId][0] && picking && !shooting)
         {
             shootCol = pickedCol;
             shootTex = pickedTex;
@@ -239,7 +238,7 @@
         }
 
 
-        if (events.buttDown[0] && !shooting && !picking)
+        if (events.buttDown[playerId][0] && !shooting && !picking)
         { // we're shoootin :)
             int p = 0;
             XY pt1 = pts[currPt - 1];
@@ -302,7 +301,7 @@
             }
         }
 
-        if (events.buttUp[0])
+        if (events.buttUp[playerId][0])
         {
             if (picking && !shooting && !reshooting)
             {
Index: zaz-0.2.9+dfsg1/src/player.h
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/player.h	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/player.h	2010-10-03 21:06:51.000000000 +0200
@@ -34,6 +34,7 @@
         static const int shootingSpeed = 3;
         static const int accuracyShotTimeout = 2300;
 
+        int playerId;
         GLuint tex;
         Bezier path;
         bool drawPath;
@@ -73,7 +74,7 @@
         int accuracyShotTime;
 
     public:
-        Player(Bezier path, BallPath **ballPaths, bool pathLooped = false, bool invert = false, bool drawPath = false);
+        Player(int id, Bezier path, BallPath **ballPaths, bool pathLooped = false, bool invert = false, bool drawPath = false);
         ~Player();
         void Move(double t);
         void Render();
Index: zaz-0.2.9+dfsg1/src/scene.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/scene.cpp	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/scene.cpp	2010-10-03 21:06:51.000000000 +0200
@@ -25,6 +25,11 @@
 
 #include <GL/gl.h>
 
+#include "SDL/SDL_syswm.h"
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/extensions/XInput2.h>
+
 namespace Scenes
 {
     Mixer *Scene::mixer = NULL;
@@ -41,6 +46,99 @@
         owns_mixer = false;
         restartFromFrame = 0;
         screenshotRequested = false;
+        xi_opcode = 0;
+        xi_relative = 0;
+        xi_devices = 0;
+
+        /* Xinput 2 setup */
+        SDL_SysWMinfo wminfo;
+        SDL_VERSION(&wminfo.version);
+        SDL_GetWMInfo(&wminfo);
+
+        wminfo.info.x11.lock_func();
+        int event, error;
+        if (!XQueryExtension(wminfo.info.x11.display, "XInputExtension", &xi_opcode, &event, &error)) {
+            printf("X Input extension not available.\n");
+        } else {
+            printf("X Input extension available.\n");
+
+            /* Enumerate master devices */
+            int masters_n = 0;
+            XIDeviceInfo *info = XIQueryDevice(wminfo.info.x11.display, XIAllMasterDevices, &masters_n);
+            int i = 0;
+            for (int j = 0; j < masters_n && i < MAX_POINTERS; j++) {
+                if (info[j].use == XIMasterPointer) {
+                    xi_device_ids[i] = info[j].deviceid;
+                    i++;
+                }
+            }
+            xi_devices = i;
+            printf("%d master pointers detected.\n", i);
+            for (; i < MAX_POINTERS; i++) {
+                xi_device_ids[i] = 0;
+            }
+
+            XIFreeDeviceInfo(info);
+
+        }
+            
+        wminfo.info.x11.unlock_func();
+    }
+
+    void Scene::GrabInput(int grab)
+    {
+        if (grab) {
+            SDL_ShowCursor(SDL_DISABLE);
+
+            SDL_SysWMinfo wminfo;
+            SDL_VERSION(&wminfo.version);
+            SDL_GetWMInfo(&wminfo);
+            wminfo.info.x11.lock_func();
+
+            XIEventMask eventmask;
+            unsigned char mask[1] = { 0 }; /* the actual mask */
+
+            eventmask.deviceid = XIAllMasterDevices;
+            eventmask.mask_len = sizeof(mask); /* always in bytes */
+            eventmask.mask = mask;
+            /* now set the mask */
+            XISetMask(mask, XI_KeyPress);
+            XISetMask(mask, XI_KeyRelease);
+            XISetMask(mask, XI_ButtonPress);
+            XISetMask(mask, XI_ButtonRelease);
+            XISetMask(mask, XI_Motion);
+
+            printf("Grabbing %d devices...\n", xi_devices);
+            for (int i=0; i<xi_devices; i++) {
+                Status ret= XIGrabDevice(wminfo.info.x11.display, xi_device_ids[i],
+                             wminfo.info.x11.window,
+                             CurrentTime, None,
+                             GrabModeAsync, GrabModeAsync,
+                             True, &eventmask);
+                if (!ret)
+                    printf("Grab on %d: %d\n", xi_device_ids[i], ret);
+            }
+            printf("done\n");
+            wminfo.info.x11.unlock_func();
+
+            xi_relative = 1;
+        } else {
+            SDL_ShowCursor(SDL_ENABLE);
+
+            SDL_SysWMinfo wminfo;
+            SDL_VERSION(&wminfo.version);
+            SDL_GetWMInfo(&wminfo);
+            wminfo.info.x11.lock_func();
+            printf("Ungrabbing devices...\n");
+            for (int i=0; i<xi_devices; i++) {
+                Status ret = XIUngrabDevice(wminfo.info.x11.display, xi_device_ids[i], CurrentTime);
+                if (!ret)
+                    printf("Ungrab on %d: %d\n", xi_device_ids[i], ret);
+            }
+            wminfo.info.x11.unlock_func();
+
+            xi_relative = 0;
+        }
     }
 
     void Scene::RestartTimers()
@@ -114,6 +212,45 @@
         }
 
         GLSetup();
+
+        /* Xinput 2 setup */
+        SDL_SysWMinfo wminfo;
+        SDL_VERSION(&wminfo.version);
+        SDL_GetWMInfo(&wminfo);
+
+        wminfo.info.x11.lock_func();
+        int event, error;
+        if (!XQueryExtension(wminfo.info.x11.display, "XInputExtension", &xi_opcode, &event, &error)) {
+            printf("X Input extension not available.\n");
+        } else {
+            printf("X Input extension available.\n");
+
+
+            XIEventMask eventmask;
+            unsigned char mask[1] = { 0 }; /* the actual mask */
+
+            eventmask.deviceid = XIAllMasterDevices;
+            eventmask.mask_len = sizeof(mask); /* always in bytes */
+            eventmask.mask = mask;
+            /* now set the mask */
+            XISetMask(mask, XI_KeyPress);
+            XISetMask(mask, XI_KeyRelease);
+            XISetMask(mask, XI_ButtonPress);
+            XISetMask(mask, XI_ButtonRelease);
+            XISetMask(mask, XI_Motion);
+
+            /* select on the window */
+            XISelectEvents(wminfo.info.x11.display, wminfo.info.x11.window, &eventmask, 1);
+
+            /* Let unhandled X events pass */
+            SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
+
+            /* Prevent SDL from messing with the mouse */
+            setenv("SDL_MOUSE_RELATIVE","0",1);
+        }
+        wminfo.info.x11.unlock_func();
+
+
         RestartTimers();
         uint last_sec = tickStart / 1000;
         uint lfpsc = 0;
@@ -289,71 +426,221 @@
     {
         SDL_Event event;
         FrameEvents ev;
+        int mx[MAX_POINTERS], my[MAX_POINTERS], rmx[MAX_POINTERS], rmy[MAX_POINTERS];
+
+        for (int i = 0; i < MAX_POINTERS; i++) {
+            mx[i] = xi_last_x[i];
+            my[i] = xi_last_y[i];
+            rmx[i] = 0;
+            rmy[i] = 0;
+        }
+
+
+        /* Catch XInput2-Events here */
+
+        SDL_SysWMinfo wminfo;
+        SDL_VERSION(&wminfo.version);
+        SDL_GetWMInfo(&wminfo);
+
+        wminfo.info.x11.lock_func();
+        // printf("Display %p, Window %p\n", wminfo.info.x11.display, wminfo.info.x11.window);
+        
+        while (XPending(wminfo.info.x11.display)) {
+            XEvent xev;
+
+            XNextEvent(wminfo.info.x11.display, &xev);
+
+            if (xev.type != GenericEvent) {
+                printf("Ignoring event of type %d\n", xev.type);
+                continue;
+            }
+
+            XGenericEventCookie *cookie = &xev.xcookie;
+            if (cookie->extension != xi_opcode) {
+                printf("Ignoring event of extension %d\n", cookie->extension);
+                continue;
+            }
+            
+            XGetEventData(wminfo.info.x11.display, cookie);
+
+            XIDeviceEvent *xievent = (XIDeviceEvent*)cookie->data;
+
+            // Find the correct device for this XI2 event
+            int i;
+            for (i = 0; i < MAX_POINTERS; i++) {
+                if (xi_device_ids[i] == xievent->deviceid) break;
+            }
+
+            switch (cookie->evtype)
+            {
+            case XI_ButtonPress:
+                if (i == MAX_POINTERS) break;
+                if (xievent->detail == Button1)
+                    ev.buttDown[i][0] = true;
+                if (xievent->detail == Button3)
+                    ev.buttDown[i][1] = true;
+                if (xievent->detail == Button2)
+                    ev.buttDown[i][2] = true;
 
-        while (SDL_PollEvent(&event))
-        {
-            switch (event.type)
-            {
-            case SDL_KEYDOWN:
-                ev.keyDown.push_back(event.key.keysym.sym);
                 ev.empty = false;
-				ev.unicode.push_back(event.key.keysym.unicode);
                 break;
-            case SDL_KEYUP:
-                ev.keyUp.push_back(event.key.keysym.sym);
+            case XI_ButtonRelease:
+                if (i == MAX_POINTERS) break;
+                if (xievent->detail == Button1)
+                    ev.buttUp[i][0] = true;
+                if (xievent->detail == Button3)
+                    ev.buttUp[i][1] = true;
+                if (xievent->detail == Button2)
+                    ev.buttUp[i][2] = true;
+
                 ev.empty = false;
                 break;
-            case SDL_MOUSEBUTTONDOWN:
-                if (event.button.button == SDL_BUTTON_LEFT)
-                    ev.buttDown[0] = true;
-                if (event.button.button == SDL_BUTTON_RIGHT)
-                    ev.buttDown[1] = true;
-                if (event.button.button == SDL_BUTTON_MIDDLE)
-                    ev.buttDown[2] = true;
+            case XI_Motion:
+                if (i == MAX_POINTERS) break;
+                rmx[i] += (int)xievent->event_x - mx[i];
+                rmy[i] += (int)xievent->event_y - my[i];
+
+                mx[i] = (int)xievent->event_x;
+                my[i] = (int)xievent->event_y;
 
                 ev.empty = false;
                 break;
 
-            case SDL_MOUSEBUTTONUP:
-                if (event.button.button == SDL_BUTTON_LEFT)
-                    ev.buttUp[0] = true;
-                if (event.button.button == SDL_BUTTON_RIGHT)
-                    ev.buttUp[1] = true;
-                if (event.button.button == SDL_BUTTON_MIDDLE)
-                    ev.buttUp[2] = true;
+            case XI_KeyPress:
+                if (!xi_relative) {
+                    printf("Unexpected key down: %d\n", xievent->detail);
+                } else {
+                    KeyCode kc = xievent->detail;
+                    KeySym xsym = XKeycodeToKeysym(wminfo.info.x11.display, kc, 0);
+                    if (xsym == XK_Escape) ev.keyDown.push_back(SDLK_ESCAPE);
 
-                ev.empty = false;
+                    ev.empty = false;
+                }
                 break;
 
-            case SDL_MOUSEMOTION:
-                ev.empty = false;
+            case XI_KeyRelease:
+                if (!xi_relative) {
+                    printf("Unexpected key up: %d\n", xievent->detail);
+                } else {
+                    KeyCode kc = xievent->detail;
+                    KeySym xsym = XKeycodeToKeysym(wminfo.info.x11.display, kc, 0);
+                    if (xsym == XK_Escape) ev.keyUp.push_back(SDLK_ESCAPE);
+
+                    ev.empty = false;
+                }
                 break;
             }
+
+#if 0
+            printf("Type: %d Extension: %d\n", cookie->type, cookie->extension);
+                printf("EVENT TYPE %d\n", cookie->evtype);
+
+            double *val;
+            int i;
+
+            printf("    device: %d (%d)\n", xievent->deviceid, xievent->sourceid);
+            printf("    detail: %d\n", xievent->detail);
+            if (xievent->flags & XIKeyRepeat)
+            printf("    xievent is a key repeat.\n");
+
+            printf("    root: %.2f/%.2f\n", xievent->root_x, xievent->root_y);
+            printf("    xievent: %.2f/%.2f\n", xievent->event_x, xievent->event_y);
+
+            printf("    buttons:");
+            for (i = 0; i < xievent->buttons.mask_len * 8; i++)
+                if (XIMaskIsSet(xievent->buttons.mask, i))
+                    printf(" %d", i);
+            printf("\n");
+
+            printf("    modifiers: locked %#x latched %#x base %#x\n",
+                xievent->mods.locked, xievent->mods.latched,
+                xievent->mods.base);
+            printf("    group: locked %#x latched %#x base %#x\n",
+                xievent->group.locked, xievent->group.latched,
+                xievent->group.base);
+            printf("    valuators:");
+
+            val = xievent->valuators.values;
+            for (i = 0; i < xievent->valuators.mask_len * 8; i++)
+                if (XIMaskIsSet(xievent->valuators.mask, i))
+                    printf(" %.2f", *val++);
+            printf("\n");
+
+            printf("    windows: root 0x%lx event 0x%lx child 0x%ld\n",
+                xievent->root, xievent->event, xievent->child);
+#endif          
+
+            XFreeEventData(wminfo.info.x11.display, cookie);
         }
+        
+        wminfo.info.x11.unlock_func();
 
-        int mx = 0;
-        int my = 0;
-        int rmx = 0;
-        int rmy = 0;
+	/* 
+        if (!xi_relative) {
+            // We are not in relative mouse mode, i.e. game mode, so
+            // leave text input to SDL
+	    // Unfortunaltey, this would ignore some XI events.
+		
+            while (SDL_PollEvent(&event))
+            {
+                switch (event.type)
+                {
+                case SDL_KEYDOWN:
+                    ev.keyDown.push_back(event.key.keysym.sym);
+                    ev.empty = false;
+                    ev.unicode.push_back(event.key.keysym.unicode);
+                    break;
+                case SDL_KEYUP:
+                    ev.keyUp.push_back(event.key.keysym.sym);
+                    ev.empty = false;
+                    break;
+                }
+            }
+        }
+	*/
 
+        /*
         if (!ev.empty)
         {
             SDL_GetMouseState(&mx, &my);
             SDL_GetRelativeMouseState(&rmx, &rmy);
         }
+        */
 
-        if (logicInputFrame == 0)
-            rmx = rmy = 0;
-
-        ev.mouseX = (double)mx / (double)surface->w;
-        ev.mouseY = (double)my / (double)surface->h;
-
-        // convert to int and back exactly as recording/playback would have done
-        ev.mouseX = (double)((int)(ev.mouseX * MOUSE_RESOLUTION)) / MOUSE_RESOLUTION;
-        ev.mouseY = (double)((int)(ev.mouseY * MOUSE_RESOLUTION)) / MOUSE_RESOLUTION;
-
-        ev.relmouseX = rmx;
-        ev.relmouseY = rmy;
+        for (int i = 0; i < MAX_POINTERS; i++) {
+            if (logicInputFrame == 0) {
+                rmx[i] = 0;
+                rmy[i] = 0;
+            }
+
+            xi_last_x[i] = mx[i];
+            xi_last_y[i] = my[i];
+
+            ev.mouseX[i] = (double)mx[i] / (double)surface->w;
+            ev.mouseY[i] = (double)my[i] / (double)surface->h;
+
+            // convert to int and back exactly as recording/playback would have done
+            ev.mouseX[i] = (double)((int)(ev.mouseX[i] * MOUSE_RESOLUTION)) / MOUSE_RESOLUTION;
+            ev.mouseY[i] = (double)((int)(ev.mouseY[i] * MOUSE_RESOLUTION)) / MOUSE_RESOLUTION;
+
+            ev.relmouseX[i] = rmx[i];
+            ev.relmouseY[i] = rmy[i];
+
+
+            /* Warp mouse to center if we want relative movements and we are close to border */
+            if (  xi_device_ids[i] && xi_relative &&
+                ( mx[i] < 8 || mx[i] > surface->w - 8 ||
+                  my[i] < 8 || my[i] > surface->h - 8)
+            ) {
+                wminfo.info.x11.lock_func();
+                XIWarpPointer(wminfo.info.x11.display, xi_device_ids[i],
+                    NULL, wminfo.info.x11.window,
+                    0,0,0,0, (double)(surface->w/2), (double)(surface->h/2));
+                xi_last_x[i] = surface->w/2;
+                xi_last_y[i] = surface->h/2;
+                wminfo.info.x11.unlock_func();
+            }
+        }
 
         return ev;
     }
@@ -411,6 +698,7 @@
             if (fields[2].size() - startpos)
                 ev.keyUp.push_back((SDLKey)atoi(fields[2].substr(startpos).c_str()));
 
+            /* TODO for multi mouse
             ev.mouseX = atoi(fields[3].c_str());
             ev.mouseY = atoi(fields[4].c_str());
 
@@ -427,6 +715,7 @@
             ev.buttUp[0] = fields[8][0] == '1';
             ev.buttUp[1] = fields[8][1] == '1';
             ev.buttUp[2] = fields[8][2] == '1';
+            */
 
             *in_stream >> nextEventLine;
         }
@@ -470,11 +759,13 @@
             }
 
             // mouse position
+            /* TODO for multiple mouses
             *out_stream << ":" << (int)(events.mouseX * MOUSE_RESOLUTION) << ":" << (int)(events.mouseY * MOUSE_RESOLUTION);
             *out_stream << ":" << events.relmouseX << ":" << events.relmouseY;
             // buttons
             *out_stream << ":" << events.buttDown[0] << events.buttDown[1] << events.buttDown[2];
             *out_stream << ":" << events.buttUp[0] << events.buttUp[1] << events.buttUp[2];
+            */
 
             *out_stream << endl;
         }
Index: zaz-0.2.9+dfsg1/src/scene.h
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/scene.h	2010-10-03 21:06:44.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/scene.h	2010-10-03 21:06:51.000000000 +0200
@@ -39,6 +39,7 @@
         protected:
             void RestartTimers();
             void SaveScreenshot(string philename);
+            void GrabInput(int grabbed);
 
             uint renderFPS;
             uint logicFPS;
@@ -67,6 +68,12 @@
         protected:
             SceneMode mode;
             SDL_Surface *surface;
+            int xi_opcode;
+            int xi_relative;
+            int xi_last_x[MAX_POINTERS];
+            int xi_last_y[MAX_POINTERS];
+            int xi_device_ids[MAX_POINTERS];
+            int xi_devices;
             bool quit;
             Settings *settings;
             FrameEvents events;
Index: zaz-0.2.9+dfsg1/src/mainmenu.cpp
===================================================================
--- zaz-0.2.9+dfsg1.orig/src/mainmenu.cpp	2010-10-03 21:06:57.000000000 +0200
+++ zaz-0.2.9+dfsg1/src/mainmenu.cpp	2010-10-03 21:07:11.000000000 +0200
@@ -586,11 +586,11 @@
             }
 
 
-        mx = (vwidth * events.mouseX) + vleft;
-        my = vheight - (vheight * events.mouseY);
+        mx = (vwidth * events.mouseX[0]) + vleft;
+        my = vheight - (vheight * events.mouseY[0]);
         bool click = false;
 
-        if (events.buttDown[0])
+        if (events.buttDown[0][0])
             click = true;
 
         currentMenu->Logic(mx, my, click);
