Index: zaz-svn/configure.ac
===================================================================
--- zaz-svn.orig/configure.ac	2010-10-05 23:19:14.000000000 +0200
+++ zaz-svn/configure.ac	2010-10-05 23:23:14.000000000 +0200
@@ -10,7 +10,7 @@
 AC_HEADER_STDC
 AC_PROG_RANLIB
 
-PKG_CHECK_MODULES(ZAZ, [sdl vorbisfile theoraenc theoradec])
+PKG_CHECK_MODULES(ZAZ, [sdl vorbisfile theoraenc theoradec xi])
 PKG_CHECK_MODULES(FTGL, ftgl >= 2.1.3)
 
 AC_CHECK_LIB(SDL_image, IMG_Load, [], AC_MSG_ERROR(no SDL_image found))
Index: zaz-svn/src/ballpath.cpp
===================================================================
--- zaz-svn.orig/src/ballpath.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/ballpath.cpp	2010-10-05 23:22:42.000000000 +0200
@@ -140,6 +140,9 @@
 
 void BallPath::Logic()
 {
+    // Reset pending accuracyShots 
+    state.accuracyShotTriggered = false;
+
     if (extraBallFading)
     {
         if (!balls.empty())
Index: zaz-svn/src/editor.cpp
===================================================================
--- zaz-svn.orig/src/editor.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/editor.cpp	2010-10-05 23:22:42.000000000 +0200
@@ -448,10 +448,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))
             {
@@ -501,8 +501,7 @@
                     Game game(surface, level, 0, 0, 0, 0, true);
                     game.Run();
                     GLSetup();
-                    SDL_ShowCursor(SDL_ENABLE);
-                    SDL_WM_GrabInput(SDL_GRAB_OFF);
+		    GrabInput(0);
                     return;
                 }
 
@@ -512,8 +511,7 @@
                     Game game(surface, level, 0, (uint)time(0), 5, 0, false, true);
                     game.Run();
                     GLSetup();
-                    SDL_ShowCursor(SDL_ENABLE);
-                    SDL_WM_GrabInput(SDL_GRAB_OFF);
+		    GrabInput(0);
                     return;
                 }
 
Index: zaz-svn/src/frame_events.cpp
===================================================================
--- zaz-svn.orig/src/frame_events.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/frame_events.cpp	2010-10-05 23:49:55.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,9 @@
 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] = buttDown[i][j] = false;
     }
 }
 
Index: zaz-svn/src/frame_events.h
===================================================================
--- zaz-svn.orig/src/frame_events.h	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/frame_events.h	2010-10-05 23:27:24.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-svn/src/game.cpp
===================================================================
--- zaz-svn.orig/src/game.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/game.cpp	2010-10-05 23:50:46.000000000 +0200
@@ -211,7 +211,14 @@
         inv = !inv;
     }
 
-    pl = new Player(nb, ballPaths, level.loop, inv, fromEditor, level.kidsMode || alwaysAccuracy);
+    for (int i = 0; i < xi_devices; i++){
+       if (xi_device_ids[i]) {
+            pl[i] = new Player(i, nb, ballPaths, level.loop, inv, fromEditor, level.kidsMode || alwaysAccuracy);
+        } else {
+            pl[i] = NULL;
+        }
+    }
+
     dying = false;
 
     sfxVol = atoi(settings.get("sfxVolume", "50").c_str());
@@ -269,7 +276,9 @@
     if (ownTextures)
         glDeleteTextures(14, ballText);
 
-    delete pl;
+    for (int i = 0; i< xi_devices; i++) {
+        delete pl[i];
+    }
 
     level.FreeTex();
 }
@@ -375,14 +384,16 @@
                     if (editor)
                     {
                         renderingForThumb = true;
-                        pl->alwaysAccuracy = false;
-                        pl->drawPath = false;
+                        pl[0]->alwaysAccuracy = false;
+                        pl[0]->drawPath = false;
                     }
                 }
             }
     }
 
-    pl->Logic(events);
+    for (int i = 0; i< xi_devices; i++) {
+        pl[i]->Logic(events);
+    }
 
     if (editor && !renderingForThumb)
     {
@@ -466,8 +477,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;
@@ -504,8 +517,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)
@@ -741,7 +756,9 @@
         ballPaths[b]->Render(false, true);
     }
 
-    pl->Render();
+    for (int i = 0; i< xi_devices; i++) {
+        pl[i]->Render();
+    }
 
     // render score
     glLoadIdentity( );
@@ -782,7 +799,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);
@@ -900,7 +917,7 @@
     if (renderingForThumb)
     {
         renderingForThumb = false;
-        pl->drawPath = false;
+        pl[0]->drawPath = false;
     }
 }
 
@@ -908,7 +925,7 @@
 {
     if ((mode == RUN) || (mode == RECORD))
     {
-        SDL_WM_GrabInput(SDL_GRAB_ON);
+        GrabInput(1);
     }
 
     int width = surface->w;
Index: zaz-svn/src/game.h
===================================================================
--- zaz-svn.orig/src/game.h	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/game.h	2010-10-05 23:29:36.000000000 +0200
@@ -70,7 +70,7 @@
     SDL_Surface *surface;
     void CleanupSounds();
 
-    Player *pl;
+    Player *pl[MAX_POINTERS];
     BallPath **ballPaths;
     int nPaths;
 
Index: zaz-svn/src/gameloop.cpp
===================================================================
--- zaz-svn.orig/src/gameloop.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/gameloop.cpp	2010-10-05 23:51:50.000000000 +0200
@@ -267,7 +267,7 @@
 
 void GameLoop::GLSetup()
 {
-    SDL_WM_GrabInput(SDL_GRAB_OFF);
+    GrabInput(0);
 
     int width = surface->w;
     int height = surface->h;
@@ -347,10 +347,10 @@
 
         // we have to translate mouse events to local coords for the menu
         FrameEvents tempEvents = events;
-        tempEvents.mouseX = mx;
-        tempEvents.mouseY = my;
+        tempEvents.mouseX[0] = mx;
+        tempEvents.mouseY[0] = my;
 
-        if (events.buttDown[0])
+        if (events.buttDown[0][0])
             click = true;
 
         if (showSureQuitMenu)
@@ -402,7 +402,7 @@
             SDL_GetMouseState(&sx, &sy);
             game->Record(tempRecording, pausedFrame);
             SDL_WarpMouse(sx, sy);
-            RecalculateMousePos();
+            //RecalculateMousePos();
             continueGame = false;
             GenScreenShotTexture();
             GLSetup();
@@ -414,7 +414,7 @@
             SDL_GetMouseState(&sx, &sy);
             game->Record(tempRecording, 0);
             SDL_WarpMouse(sx, sy);
-            RecalculateMousePos();
+            //RecalculateMousePos();
             GenScreenShotTexture();
             GLSetup();
         }
Index: zaz-svn/src/player.cpp
===================================================================
--- zaz-svn.orig/src/player.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/player.cpp	2010-10-05 23:50:07.000000000 +0200
@@ -20,8 +20,8 @@
 #include "game.h"
 #include <deque>
 
-Player::Player(Bezier path, BallPath **ballPaths, bool pathLooped, bool invert, bool drawPath, bool alwaysAccuracy)
-        : 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, bool alwaysAccuracy)
+        : 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), alwaysAccuracy(alwaysAccuracy), disableSpeedup(settings.getb("disableSpeedup", false))
 {
@@ -119,7 +119,6 @@
         {
             accuracyShot = true;
             accuracyShotTime = accuracyShotTimeout;
-            ballPaths[p]->state.accuracyShotTriggered = false;
         }
 
         p++;
@@ -292,12 +291,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] || buttPressed) && picking && !shooting)
+            if ((events.buttDown[playerId][0] || buttPressed) && picking && !shooting)
             {
                 shootCol = pickedCol;
                 shootTex = pickedTex;
@@ -317,18 +316,18 @@
                 mix->EnqueueSample(sfx_push, sfxVol);
             }
 
-            if (events.buttDown[1])
+            if (events.buttDown[playerId][1])
             {
                 speedUp = 1;
             }
 
-            if (events.buttUp[1])
+            if (events.buttUp[playerId][1])
             {
                 speedUp = -1;
             }
 
 
-            if ((events.buttDown[0] || buttPressed) && !shooting && !picking)
+            if ((events.buttDown[playerId][0] || buttPressed) && !shooting && !picking)
             { // we're shoootin :)
                 int p = 0;
                 XY pt1 = pts[currPt - 1];
@@ -391,7 +390,7 @@
                 }
             }
 
-            if (events.buttUp[0] || buttReleased)
+            if (events.buttUp[playerId][0] || buttReleased)
             {
                 if (picking && !shooting && !reshooting)
                 {
Index: zaz-svn/src/player.h
===================================================================
--- zaz-svn.orig/src/player.h	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/player.h	2010-10-05 23:33:22.000000000 +0200
@@ -34,6 +34,7 @@
     static const int shootingSpeed = 3;
     static const int accuracyShotTimeout = 2300;
 
+    int playerId;
     GLuint tex;
     Bezier path;
     bool drawPath;
@@ -81,7 +82,7 @@
     bool disableSpeedup;
 
 public:
-    Player(Bezier path, BallPath **ballPaths, bool pathLooped = false, bool invert = false, bool drawPath = false, bool alwaysAccuracy = false);
+    Player(int id, Bezier path, BallPath **ballPaths, bool pathLooped = false, bool invert = false, bool drawPath = false, bool alwaysAccuracy = false);
     ~Player();
     void Move(double t);
     void Render();
Index: zaz-svn/src/scene.cpp
===================================================================
--- zaz-svn.orig/src/scene.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/scene.cpp	2010-10-06 00:10:43.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 *mixer = NULL;
@@ -40,11 +45,106 @@
     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();
+
     vwidth = 100 * (640.0/480.0);
     vleft = (100 - vwidth) / 2;
     vheight = 100.0;
 }
 
+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()
 {
     tickStart = SDL_GetTicks() - ((restartFromFrame * 1000) / desiredFPS);
@@ -121,6 +221,44 @@
     }
 
     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;
@@ -309,62 +447,155 @@
 
 FrameEvents Scene::CreateFrameEvents()
 {
-    SDL_Event event;
     FrameEvents ev;
-
     bool mmotion = false;
-    int mmx = 0;
-    int mmy = 0;
-    int rmx = 0;
-    int rmy = 0;
+    int mmx[MAX_POINTERS], mmy[MAX_POINTERS], rmx[MAX_POINTERS], rmy[MAX_POINTERS];
+
+    for (int i = 0; i < MAX_POINTERS; i++) {
+        mmx[i] = xi_last_x[i];
+        mmy[i] = xi_last_y[i];
+        rmx[i] = 0;
+        rmy[i] = 0;
+    }
 
+    /* Catch XInput2-Events here */
 
-    while (SDL_PollEvent(&event))
-    {
-        switch (event.type)
-        {
-        case SDL_KEYDOWN:
-            ev.keyDown.push_back(event.key.keysym.sym);
+    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;
             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 - mmx[i];
+            rmy[i] += (int)xievent->event_y - mmy[i];
+
+            mmx[i] = (int)xievent->event_x;
+            mmy[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:
-            mmx = event.motion.x;
-            mmy = event.motion.y;
-            rmx = event.motion.xrel;
-            rmy = event.motion.yrel;
+        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);
 
-            mmotion = true;
+                ev.empty = false;
+            }
             break;
         }
+        XFreeEventData(wminfo.info.x11.display, cookie);
     }
 
+    wminfo.info.x11.unlock_func();
+
+    for (int i = 0; i < MAX_POINTERS; i++) {
+        if (logicInputFrame == 0) {
+            rmx[i] = 0;
+            rmy[i] = 0;
+        }
+
+        xi_last_x[i] = mmx[i];
+        xi_last_y[i] = mmy[i];
+
+        ev.mouseX[i] = (double)mmx[i] / (double)surface->w;
+        ev.mouseY[i] = (double)mmy[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 &&
+            ( mmx[i] < 8 || mmx[i] > surface->w - 8 ||
+              mmy[i] < 8 || mmy[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();
+        }
+    }
+
+
+    mx = (vwidth * ev.mouseX[0]) + vleft;
+    my = vheight - (vheight * ev.mouseY[0]);
+
+/*
+    
     if (mmotion)
     {
         if (logicInputFrame == 0)
@@ -383,10 +614,12 @@
         RecalculateMousePos(mmx, mmy);
         ev.empty = false;
     }
+*/
 
     return ev;
 }
 
+/*
 void Scene::RecalculateMousePos(int mmx, int mmy)
 {
     if ((mmx == mmy) && (mmx == -1))
@@ -403,7 +636,9 @@
 
     mx = (vwidth * mouseX) + vleft;
     my = vheight - (vheight * mouseY);
+
 }
+*/
 
 
 FrameEvents Scene::LoadFrameEvents()
@@ -459,6 +694,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());
 
@@ -475,6 +711,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;
     }
@@ -518,11 +755,13 @@
         }
 
         // mouse position
+        /* TODO for multi mouse
         *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-svn/src/scene.h
===================================================================
--- zaz-svn.orig/src/scene.h	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/scene.h	2010-10-05 23:47:17.000000000 +0200
@@ -41,6 +41,7 @@
 protected:
     void RestartTimers();
     void SaveScreenshot(string philename);
+    void GrabInput(int grabbed);
 
     uint renderFPS;
     uint logicFPS;
@@ -55,6 +56,14 @@
     double vleft;
     double vheight;
 
+    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;
+
+
     double logicInterval;
     bool resync;
     bool screenshotRequested;
@@ -63,7 +72,7 @@
     FrameEvents CreateFrameEvents();
     FrameEvents LoadFrameEvents();
     void SaveFrameEvents();
-    void RecalculateMousePos(int mmx = -1, int mmy = -1);
+    // void RecalculateMousePos(int mmx = -1, int mmy = -1);
 
     string nextEventLine;
 
Index: zaz-svn/src/mainmenu.cpp
===================================================================
--- zaz-svn.orig/src/mainmenu.cpp	2010-10-05 23:11:29.000000000 +0200
+++ zaz-svn/src/mainmenu.cpp	2010-10-06 00:04:10.000000000 +0200
@@ -1220,7 +1220,7 @@
         lmx = mx;
         lmy = my;
 
-        if (events.buttDown[0])
+        if (events.buttDown[0][0])
         {
             if (browserMouseOverLevel != -1)
                 click = true;
@@ -1228,8 +1228,8 @@
 
         // we have to translate mouse events to local coords for the menu
         FrameEvents tempEvents = events;
-        tempEvents.mouseX = mx;
-        tempEvents.mouseY = my;
+        tempEvents.mouseX[0] = mx;
+        tempEvents.mouseY[0] = my;
 
         if (showProfiles)
         {
@@ -1288,7 +1288,7 @@
 
             GameLoop(surface, ballText, browserSelectedLevel, Scenes::DEFAULT_FPS, survival).Run();
             resync = true;
-            RecalculateMousePos();
+            //RecalculateMousePos();
 
             StartMusic();
             GLSetup();
Index: zaz-svn/src/menu.cpp
===================================================================
--- zaz-svn.orig/src/menu.cpp	2010-10-06 00:12:06.000000000 +0200
+++ zaz-svn/src/menu.cpp	2010-10-06 00:12:30.000000000 +0200
@@ -122,7 +122,7 @@
     if (lmy != my)
         mouseMoved = true;
 
-    if (events.buttDown[0])
+    if (events.buttDown[0][0])
     {
         click = true;
         mouseMoved = true;
