summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Spilsbury <Sam@XPS-SUSE.site>2008-11-01 13:32:11 +0900
committerSam Spilsbury <Sam@XPS-SUSE.site>2008-11-01 13:32:11 +0900
commit7df5326d0f2d13e78fa1333bdc2d06a3735208fa (patch)
treea10815cc724d54d2020565b0ab7265df52858f0c
parent02521a6331a5f086a274af4052a0c24c3ccd6149 (diff)
downloadcompiz-mpx-ir-7df5326d0f2d13e78fa1333bdc2d06a3735208fa.tar.gz
compiz-mpx-ir-7df5326d0f2d13e78fa1333bdc2d06a3735208fa.tar.bz2
Add Compiz Core Input Redirection and MPX Patches
-rw-r--r--compiz/0001-X-Input-2-Support.patch1405
-rw-r--r--compiz/0002-X-Input-2-support-in-plugins.patch3418
-rw-r--r--compiz/0003-XComposite-Triangular-Co-ordinate-Mesh-Input-Redirec.patch948
-rw-r--r--compiz/0004-CompMesh-based-Input-Redirection-support.patch746
4 files changed, 6517 insertions, 0 deletions
diff --git a/compiz/0001-X-Input-2-Support.patch b/compiz/0001-X-Input-2-Support.patch
new file mode 100644
index 0000000..43cb71e
--- /dev/null
+++ b/compiz/0001-X-Input-2-Support.patch
@@ -0,0 +1,1405 @@
+From 96731e7238c06d7b5b4f2ab2b57769ccb86670c4 Mon Sep 17 00:00:00 2001
+From: Sam Spilsbury <Sam@XPS-SUSE.site>
+Date: Fri, 31 Oct 2008 10:28:17 +0900
+Subject: [PATCH] X Input 2 Support
+
+---
+ configure.ac | 2 +
+ include/compiz-core.h | 83 +++++++++
+ include/compiz-scale.h | 12 +-
+ src/Makefile.am | 3 +-
+ src/devices.c | 480 ++++++++++++++++++++++++++++++++++++++++++++++++
+ src/display.c | 68 +++++++-
+ src/event.c | 304 +++++++++++++++++++++++++++----
+ src/window.c | 41 ++++-
+ 8 files changed, 951 insertions(+), 42 deletions(-)
+ create mode 100644 src/devices.c
+
+diff --git a/configure.ac b/configure.ac
+index 4532099..bbfdb77 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -124,10 +124,12 @@ COMPIZ_REQUIRES="x11-xcb \
+ xdamage \
+ xrandr \
+ xinerama \
++ [xi >= 1.1.99.1] \
+ ice \
+ sm \
+ libxml-2.0 \
+ libxslt \
++ glu \
+ libstartup-notification-1.0 >= 0.7"
+
+ PKG_CHECK_MODULES(COMPIZ, $COMPIZ_REQUIRES)
+diff --git a/include/compiz-core.h b/include/compiz-core.h
+index 5aeb04c..709f2ac 100644
+--- a/include/compiz-core.h
++++ b/include/compiz-core.h
+@@ -38,6 +38,7 @@
+ #include <X11/extensions/Xdamage.h>
+ #include <X11/extensions/Xcomposite.h>
+ #include <X11/extensions/Xinerama.h>
++#include <X11/extensions/XInput.h>
+ #include <X11/extensions/sync.h>
+ #include <X11/Xregion.h>
+ #include <X11/XKBlib.h>
+@@ -80,6 +81,7 @@ typedef struct _CompCursor CompCursor;
+ typedef struct _CompMatch CompMatch;
+ typedef struct _CompOutput CompOutput;
+ typedef struct _CompWalker CompWalker;
++typedef struct _CompDevice CompDevice;
+
+ /* virtual modifiers */
+
+@@ -1091,6 +1093,19 @@ struct _CompDisplay {
+ MatchPropertyChangedProc matchPropertyChanged;
+
+ LogMessageProc logMessage;
++
++ /* XInput devices and event types*/
++ CompDevice *devices;
++ int xi_btpress;
++ int xi_btrelease;
++ int xi_motion;
++ int xi_kpress;
++ int xi_krelease;
++ int xi_presence;
++ int xi_enter;
++ int xi_leave;
++ int xi_focusin;
++ int xi_focusout;
+ };
+
+ #define GET_CORE_DISPLAY(object) ((CompDisplay *) (object))
+@@ -3492,6 +3507,74 @@ compReadXmlChunkFromMetadataOptionInfo (const CompMetadataOptionInfo *info,
+ char *buffer,
+ int length);
+
++/* devices.c */
++struct _CompDevice {
++ XDevice *dev;
++ int id;
++ int use;
++ int paired;
++ XEventClass cls_btpress;
++ XEventClass cls_btrelease;
++ XEventClass cls_motion;
++ XEventClass cls_enter;
++ XEventClass cls_leave;
++ XEventClass cls_kpress;
++ XEventClass cls_krelease;
++ XEventClass cls_focusin;
++ XEventClass cls_focusout;
++
++ CompGrab *grabs;
++ int grabSize;
++ int maxGrab;
++
++ CompButtonGrab *buttonGrab;
++ int nButtonGrab;
++ CompKeyGrab *keyGrab;
++ int nKeyGrab;
++
++ int pointerX;
++ int pointerY;
++ int lastPointerX;
++ int lastPointerY;
++};
++
++Bool
++compInitDeviceList (CompDisplay *display);
++
++Bool
++compAddDevice (CompDisplay *display,
++ int deviceid);
++void
++compRemoveDevice(CompDisplay *display,
++ int deviceid);
++
++CompDevice*
++compFindDeviceById (CompDisplay *display,
++ int id);
++
++int
++pushDeviceGrab (CompScreen *s,
++ CompDevice *device,
++ Cursor cursor,
++ const char *name);
++void
++removeDeviceGrab(CompScreen *s,
++ CompDevice *device,
++ int index,
++ XPoint *restorePointer);
++void
++warpDevicePointer(CompScreen *s,
++ CompDevice *dev,
++ int x,
++ int y);
++Bool
++otherDeviceGrabExist (CompDevice *dev,
++ ...);
++
++void
++compRegisterXIEvents(CompDisplay *d,
++ Window w);
++
+
+ COMPIZ_END_DECLS
+
+diff --git a/include/compiz-scale.h b/include/compiz-scale.h
+index 603612f..118c27d 100644
+--- a/include/compiz-scale.h
++++ b/include/compiz-scale.h
+@@ -77,6 +77,14 @@ typedef struct _SlotArea {
+ #define SCALE_DISPLAY_OPTION_BUTTON_BINDINGS_TOGGLE 17
+ #define SCALE_DISPLAY_OPTION_NUM 18
+
++typedef struct _ScaleDevice {
++ CompDevice *dev;
++ int grabIndex;
++ Window hoveredWindow;
++ Window selectedWindow;
++ struct _ScaleDevice *next;
++} ScaleDevice;
++
+ typedef struct _ScaleDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+@@ -122,7 +130,7 @@ typedef void (*ScalePaintDecorationProc) (CompWindow *w,
+ Region region,
+ unsigned int mask);
+
+-typedef void (*ScaleSelectWindowProc) (CompWindow *w);
++typedef void (*ScaleSelectWindowProc) (CompWindow *w, ScaleDevice *sDev);
+
+ typedef struct _ScaleScreen {
+ int windowPrivateIndex;
+@@ -161,6 +169,8 @@ typedef struct _ScaleScreen {
+ int windowsSize;
+ int nWindows;
+
++ ScaleDevice *devices;
++
+ GLushort opacity;
+
+ ScaleType type;
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 683d695..8505f7b 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -29,4 +29,5 @@ compiz_SOURCES = \
+ matrix.c \
+ cursor.c \
+ match.c \
+- metadata.c
++ metadata.c \
++ devices.c
+diff --git a/src/devices.c b/src/devices.c
+new file mode 100644
+index 0000000..e27828e
+--- /dev/null
++++ b/src/devices.c
+@@ -0,0 +1,480 @@
++/*
++ * Copyright © 2008 University of South Australia
++ *
++ * Permission to use, copy, modify, distribute, and sell this software
++ * and its documentation for any purpose is hereby granted without
++ * fee, provided that the above copyright notice appear in all copies
++ * and that both that copyright notice and this permission notice
++ * appear in supporting documentation, and that the name of
++ * the authors not be used in advertising or publicity pertaining to
++ * distribution of the software without specific, written prior permission.
++ * The authors make no representations about the suitability of this
++ * software for any purpose. It is provided "as is" without express or
++ * implied warranty.
++ *
++ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
++ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
++ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
++ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
++ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ * Author: Peter Hutterer <peter@cs.unisa.edu.au>
++ */
++
++#ifdef HAVE_CONFIG_H
++# include "../config.h"
++#endif
++
++#include <string.h>
++
++#include <compiz-core.h>
++
++extern Bool inHandleEvent;
++
++static Bool
++addDevice(CompDisplay *dpy, XDeviceInfo *info)
++{
++ int ndevices = 0,
++ cls;
++ CompDevice *d;
++ XAnyClassInfo *any;
++
++ if (info->use >= IsXExtensionDevice) /* don't care about SDs */
++ return FALSE;
++
++ if (!dpy->devices)
++ {
++ dpy->devices = calloc(2, sizeof(CompDevice));
++ if (!dpy->devices)
++ return FALSE;
++ ndevices = 1;
++ d = dpy->devices;
++ } else
++ {
++ d = dpy->devices;
++ while(d->id != -1)
++ {
++ d++;
++ ndevices++;
++ }
++ dpy->devices = /* + new device, + termination */
++ realloc(dpy->devices, (ndevices + 2) * sizeof(CompDevice));
++ if (!dpy->devices)
++ return FALSE;
++
++ d = &dpy->devices[ndevices];
++ }
++
++
++ d->dev = XOpenDevice(dpy->display, info->id);
++ d->id = info->id;
++ d->use = info->use;
++ d->maxGrab = 0;
++ d->grabSize = 0;
++ d->grabs = NULL;
++ d->pointerX = 0;
++ d->pointerY = 0;
++ fprintf(stderr, "initializing button grabs\n");
++ d->buttonGrab = 0;
++ d->nButtonGrab = 0;
++ d->keyGrab = 0;
++ d->nKeyGrab = 0;
++ ndevices++;
++
++ if (info->use == IsXPointer)
++ {
++ Window root, child;
++ int winx, winy, mask;
++ XQueryDevicePointer(dpy->display, d->dev,
++ RootWindow(dpy->display, 0),
++ &root, &child,
++ &d->pointerX, &d->pointerY,
++ &winx, &winy, &mask);
++ DeviceMotionNotify (d->dev,
++ dpy->xi_motion,
++ d->cls_motion);
++ DeviceButtonPress (d->dev,
++ dpy->xi_btpress,
++ d->cls_btpress);
++ DeviceButtonRelease(d->dev,
++ dpy->xi_btrelease,
++ d->cls_btrelease);
++ DeviceEnterNotify (d->dev,
++ dpy->xi_enter,
++ d->cls_enter);
++ DeviceLeaveNotify (d->dev,
++ dpy->xi_leave,
++ d->cls_leave);
++ compLogMessage("devices", CompLogLevelDebug,
++ "Found pointer %s.\n", info->name);
++ } else if (info->use == IsXKeyboard)
++ {
++ DeviceKeyPress (d->dev,
++ dpy->xi_kpress,
++ d->cls_kpress);
++ DeviceKeyRelease(d->dev,
++ dpy->xi_krelease,
++ d->cls_krelease);
++ DeviceFocusIn (d->dev,
++ dpy->xi_focusin,
++ d->cls_focusin);
++ DeviceFocusOut (d->dev,
++ dpy->xi_focusout,
++ d->cls_focusout);
++ compLogMessage("devices", CompLogLevelDebug,
++ "Found keyboard %s.\n", info->name);
++ }
++
++ any = info->inputclassinfo;
++ for (cls = 0; cls < info->num_classes; cls++)
++ {
++ if (any->class == AttachClass)
++ {
++ d->paired = ((XAttachInfoPtr)any)->attached;
++ break;
++ }
++ any = (XAnyClassPtr)((char*)any + any->length);
++ }
++
++ /* sort-of NULL-terminate the list */
++ d[1].id = -1;
++ d[1].use = -1;
++
++ return TRUE;
++}
++
++Bool
++compInitDeviceList(CompDisplay *display)
++{
++ int ninfo, i;
++ XDeviceInfo *info = NULL;
++ XExtensionVersion *ext;
++ int ret = TRUE;
++
++ display->devices = NULL;
++ display->xi_btpress = 0;
++ display->xi_btrelease = 0;
++ display->xi_motion = 0;
++ display->xi_kpress = 0;
++ display->xi_krelease = 0;
++
++ ext = XQueryInputVersion(display->display, XI_2_Major, XI_2_Minor);
++ if (ext->major_version < XI_2_Major)
++ {
++ ret = FALSE;
++ compLogMessage("devices", CompLogLevelWarn,
++ "XI 2 not available.\n");
++ goto unwind;
++ }
++
++ info = XListInputDevices(display->display, &ninfo);
++
++ for (i = 0; i < ninfo; i++)
++ {
++ if (info[i].use < IsXExtensionDevice)
++ addDevice(display, &info[i]);
++ }
++
++unwind:
++ XFree(ext);
++ XFreeDeviceList(info);
++ return ret;
++}
++
++/**
++ * Add the device with the given deviceid to compiz' internal device list.
++ */
++Bool
++compAddDevice(CompDisplay *d, int deviceid)
++{
++ XDeviceInfo *info = NULL;
++ int ninfo, i, ret = FALSE;
++
++ info = XListInputDevices(d->display, &ninfo);
++
++ for (i = 0; i < ninfo; i++)
++ {
++ if (info[i].id == deviceid)
++ {
++ ret = addDevice(d, &info[i]);
++ break;
++ }
++ }
++
++ XFreeDeviceList(info);
++ return ret;
++}
++
++void
++compRemoveDevice(CompDisplay *dpy, int deviceid)
++{
++ CompDevice *d = dpy->devices;
++ CompDevice *remove = NULL;
++ int idx, ndevices = 0;
++
++ while(d && d->id != -1)
++ {
++ if (d->id == deviceid)
++ {
++ remove = d;
++ idx = ndevices;
++ }
++ ndevices++;
++ d++;
++ }
++
++ /* Reminder: the -1 termination device isn't counted in ndevices */
++ if (remove)
++ {
++ XCloseDevice(dpy->display, remove->dev);
++ memmove(remove, &remove[1], (ndevices - idx) * sizeof(CompDevice));
++ dpy->devices = realloc(dpy->devices, ndevices * sizeof(CompDevice));
++ }
++}
++
++
++
++/**
++ * Find and return the device with the given id or NULL if the id is invalid.
++ */
++CompDevice*
++compFindDeviceById(CompDisplay *d, int id)
++{
++ CompDevice *ret = d->devices;
++
++ while(ret && ret->id != -1)
++ {
++ if (ret->id == id)
++ return ret;
++ ret++;
++ }
++
++ return NULL;
++}
++
++
++int
++pushDeviceGrab(CompScreen *s,
++ CompDevice *device,
++ Cursor cursor,
++ const char *name)
++{
++ CompDevice *ptr, *kbd;
++
++ if (device->use == IsXPointer)
++ {
++ ptr = device;
++ kbd = compFindDeviceById(s->display, device->paired);
++ } else
++ {
++ ptr = compFindDeviceById(s->display, device->paired);
++ kbd = device;
++ }
++
++ if (ptr->maxGrab == 0)
++ {
++ int status;
++ status = XExtendedGrabDevice(s->display->display,
++ ptr->dev,
++ s->grabWindow,
++ GrabModeAsync,
++ FALSE,
++ NULL,
++ cursor,
++ 3, &ptr->cls_btpress,
++ 0, NULL);
++
++ if (status == GrabSuccess)
++ {
++ status = XGrabDevice(s->display->display, kbd->dev,
++ s->grabWindow, FALSE,
++ 2, &kbd->cls_kpress,
++ GrabModeAsync, GrabModeAsync, CurrentTime);
++ if (status != GrabSuccess)
++ {
++ XUngrabDevice(s->display->display, ptr->dev, CurrentTime);
++ return 0;
++ }
++ } else
++ return 0;
++ }
++ else
++ return 0;
++
++ if (ptr->grabSize <= ptr->maxGrab)
++ {
++ ptr->grabs = realloc(ptr->grabs,
++ sizeof(CompGrab) * (ptr->maxGrab + 1));
++ if (!ptr->grabs)
++ return 0;
++ ptr->grabSize = ptr->maxGrab + 1;
++ }
++
++ /* In theory, ptr & kbd grabs should always be in sync, so we only update
++ * ptr's grab fields */
++ ptr->grabs[ptr->maxGrab].active = TRUE;
++ ptr->grabs[ptr->maxGrab].name = name;
++ ptr->maxGrab++;
++ return ptr->maxGrab;
++}
++
++void
++removeDeviceGrab(CompScreen *s, CompDevice *device, int index,
++ XPoint *restorePointer)
++{
++ CompDevice *ptr, *kbd;
++ int maxGrab;
++ index--;
++
++ if (device->use == IsXPointer)
++ {
++ ptr = device;
++ kbd = compFindDeviceById(s->display, device->paired);
++ } else
++ {
++ ptr = compFindDeviceById(s->display, device->paired);
++ kbd = device;
++ }
++
++ ptr->grabs[index].active = FALSE;
++ for (maxGrab = ptr->maxGrab; maxGrab; maxGrab--)
++ if (ptr->grabs[maxGrab - 1].active)
++ break;
++
++ if (maxGrab != ptr->maxGrab)
++ {
++ /* FIXME: ChangeActivePointerGrab equivalent? */
++ if (!maxGrab)
++ {
++ if (restorePointer)
++ warpDevicePointer(s, ptr,
++ restorePointer->x - ptr->pointerX,
++ restorePointer->y - ptr->pointerY);
++ XUngrabDevice(s->display->display, ptr->dev, CurrentTime);
++ XUngrabDevice(s->display->display, kbd->dev, CurrentTime);
++ }
++
++ ptr->maxGrab = maxGrab;
++ }
++}
++
++void
++warpDevicePointer(CompScreen *s, CompDevice *dev, int dx, int dy)
++{
++ CompDisplay *display = s->display;
++
++ dev->pointerX += dx;
++ dev->pointerX += dy;
++
++ if (dev->pointerX >= s->width)
++ dev->pointerX = s->width - 1;
++ else if (dev->pointerX < 0)
++ dev->pointerX = 0;
++
++ if (dev->pointerY >= s->height)
++ dev->pointerY = s->height - 1;
++ else if (dev->pointerY < 0)
++ dev->pointerY = 0;
++
++ XWarpDevicePointer(display->display,
++ dev->dev,
++ None, s->root,
++ 0, 0, 0, 0,
++ dx, dy);
++ XSync(display->display, FALSE);
++
++ /* TODO: XCheckMaskEvent equivalent */
++
++ if (!inHandleEvent)
++ {
++ dev->lastPointerX = dev->pointerX;
++ dev->lastPointerY = dev->pointerY;
++ }
++}
++
++/* otherScreenGrabExist takes a series of strings terminated by a NULL.
++ It returns TRUE if a grab exists but it is NOT held by one of the
++ plugins listed, returns FALSE otherwise. */
++
++Bool
++otherDeviceGrabExist (CompDevice *dev, ...)
++{
++ va_list ap;
++ char *name;
++ int i;
++
++ for (i = 0; dev && i < dev->maxGrab; i++)
++ {
++ if (dev->grabs[i].active)
++ {
++ va_start(ap, dev);
++ name = va_arg (ap, char *);
++ while(name)
++ {
++ if (strcmp(name, dev->grabs[i].name) == 0)
++ break;
++
++ name = va_arg(ap, char*);
++ }
++ va_end(ap);
++ if (!name)
++ return TRUE;
++ }
++ }
++ return FALSE;
++}
++
++/* Register for all XI events (including presence) on all root windows */
++void
++compRegisterXIEvents(CompDisplay *d, Window w)
++{
++ XEventClass *allclasses = NULL;
++ int nclasses = 0;
++ int idx = 0;
++ CompDevice *dev;
++ XEventClass cls_presence;
++
++ nclasses = 1; /* device presence */
++
++ dev = d->devices;
++ while(dev->id != -1)
++ {
++ nclasses += 4; /* button press/rel and enter/leave,
++ or key press/rel and focus in/out,
++ depending on dev->use */
++ dev++;
++ }
++ allclasses = malloc(nclasses * sizeof(XEventClass));
++
++ if (!allclasses)
++ return;
++
++ DevicePresence(d->display, d->xi_presence, cls_presence);
++ allclasses[idx++] = cls_presence;
++
++ dev = d->devices;
++ while(dev->id != -1)
++ {
++
++ if (dev->use == IsXPointer)
++ {
++ allclasses[idx++] = dev->cls_btpress;
++ allclasses[idx++] = dev->cls_btrelease;
++ allclasses[idx++] = dev->cls_enter;
++ allclasses[idx++] = dev->cls_leave;
++
++ } else if (dev->use == IsXKeyboard)
++ {
++ allclasses[idx++] = dev->cls_kpress;
++ allclasses[idx++] = dev->cls_krelease;
++ allclasses[idx++] = dev->cls_focusin;
++ allclasses[idx++] = dev->cls_focusout;
++ }
++ dev++;
++ }
++
++ XSelectExtensionEvent(d->display, w, allclasses, nclasses);
++ free(allclasses);
++}
+diff --git a/src/display.c b/src/display.c
+index dd4676e..5cd29ad 100644
+--- a/src/display.c
++++ b/src/display.c
+@@ -52,7 +52,7 @@ static unsigned int virtualModMask[] = {
+ static CompScreen *targetScreen = NULL;
+ static CompOutput *targetOutput;
+
+-static Bool inHandleEvent = FALSE;
++Bool inHandleEvent = FALSE;
+
+ static const CompTransform identity = {
+ {
+@@ -1477,6 +1477,7 @@ eventLoop (void)
+ CompScreen *s;
+ CompWindow *w;
+ CompTimeout *t;
++ CompDevice *dev;
+ int time, timeToNextRedraw = 0;
+ unsigned int damageMask, mask;
+
+@@ -1498,6 +1499,8 @@ eventLoop (void)
+ {
+ XNextEvent (d->display, &event);
+
++ dev = NULL;
++
+ switch (event.type) {
+ case ButtonPress:
+ case ButtonRelease:
+@@ -1525,6 +1528,43 @@ eventLoop (void)
+ pointerY = event.xclient.data.l[2] & 0xffff;
+ }
+ default:
++ if (event.type == d->xi_motion)
++ {
++ XDeviceMotionEvent* mev = (XDeviceMotionEvent*)&event;
++ dev = compFindDeviceById(d, mev->deviceid);
++
++ dev->pointerX = mev->x_root;
++ dev->pointerY = mev->y_root;
++ break;
++ }
++ if (event.type == d->xi_btpress ||
++ event.type == d->xi_btrelease)
++ {
++ XDeviceButtonEvent* bev = (XDeviceButtonEvent*)&event;
++ dev = compFindDeviceById(d, bev->deviceid);
++
++ dev->pointerX = bev->x_root;
++ dev->pointerY = bev->y_root;
++ break;
++ }
++ if (event.type == d->xi_presence)
++ {
++ XDevicePresenceNotifyEvent* pev;
++ pev = (XDevicePresenceNotifyEvent*)&event;
++ if (pev->devchange == DeviceAdded)
++ {
++ int screen = 0;
++
++ compAddDevice(d, pev->deviceid);
++
++ /* Need to re-register for events on the root
++ * windows */
++ while(screen < ScreenCount(d->display))
++ compRegisterXIEvents(d,
++ XRootWindow(d->display, screen++));
++
++ }
++ }
+ break;
+ }
+
+@@ -1536,8 +1576,22 @@ eventLoop (void)
+
+ inHandleEvent = FALSE;
+
++ /* Remove the device AFTER the plugins have done their bits */
++ if (event.type == d->xi_presence)
++ {
++ XDevicePresenceNotifyEvent* pev;
++ pev = (XDevicePresenceNotifyEvent*)&event;
++ if (pev->devchange == DeviceRemoved)
++ compRemoveDevice(d, pev->deviceid);
++ }
++
+ lastPointerX = pointerX;
+ lastPointerY = pointerY;
++ for (dev = d->devices; dev->id != -1; dev++)
++ {
++ dev->lastPointerX = dev->pointerX;
++ dev->lastPointerY = dev->pointerY;
++ }
+ }
+ }
+
+@@ -2296,6 +2350,8 @@ addDisplay (const char *name)
+
+ addDisplayToCore (d);
+
++ compInitDeviceList (d);
++
+ /* TODO: bailout properly when objectInitPlugins fails */
+ assert (objectInitPlugins (&d->base));
+
+@@ -2459,6 +2515,16 @@ addDisplay (const char *name)
+
+ XGrabServer (dpy);
+
++ if (d->devices)
++ {
++ XSelectInput (dpy, XRootWindow (dpy, i),
++ SubstructureRedirectMask |
++ SubstructureNotifyMask |
++ StructureNotifyMask |
++ PropertyChangeMask |
++ ExposureMask);
++ compRegisterXIEvents(d, XRootWindow(dpy, i));
++ } else
+ XSelectInput (dpy, XRootWindow (dpy, i),
+ SubstructureRedirectMask |
+ SubstructureNotifyMask |
+diff --git a/src/event.c b/src/event.c
+index 1f34243..4dbcc1b 100644
+--- a/src/event.c
++++ b/src/event.c
+@@ -207,13 +207,20 @@ isCallBackBinding (CompOption *option,
+ CompActionState state)
+ {
+ if (!isActionOption (option))
++ {
+ return FALSE;
++ }
+
+ if (!(option->value.action.type & type))
++ {
+ return FALSE;
++ }
++
+
+ if (!(option->value.action.state & state))
++ {
+ return FALSE;
++ }
+
+ return TRUE;
+ }
+@@ -228,7 +235,9 @@ isInitiateBinding (CompOption *option,
+ return FALSE;
+
+ if (!option->value.action.initiate)
++ {
+ return FALSE;
++ }
+
+ *action = &option->value.action;
+
+@@ -256,7 +265,7 @@ static Bool
+ triggerButtonPressBindings (CompDisplay *d,
+ CompOption *option,
+ int nOption,
+- XButtonEvent *event,
++ XEvent *event,
+ CompOption *argument,
+ int nArgument)
+ {
+@@ -265,19 +274,50 @@ triggerButtonPressBindings (CompDisplay *d,
+ unsigned int modMask = REAL_MOD_MASK & ~d->ignoredModMask;
+ unsigned int bindMods;
+ unsigned int edge = 0;
++ unsigned int button, mods;
++ Window root, window;
++
++ if (event->type == ButtonPress)
++ {
++ button = event->xbutton.button;
++ mods = event->xbutton.state;
++ root = event->xbutton.root;
++ window = event->xbutton.window;
++ } else if (event->type == d->xi_btpress)
++ {
++ button = ((XDeviceButtonEvent*)event)->button;
++ mods = ((XDeviceButtonEvent*)event)->state;
++ root = ((XDeviceButtonEvent*)event)->root;
++ window = ((XDeviceButtonEvent*)event)->window;
++ } else
++ {
++ button = 0;
++ mods = 0;
++ root = 0;
++ window = 0;
++ }
+
++ /* XI Enter / Leave events
++ * don't work at the moment,
++ * so ignore edge buttons
++ * if we got an XI event
++ *
++ * FIXME: Fix xi_enter | leave
++ * events
++ */
++ if (!(event->type == d->xi_btpress))
+ if (edgeWindow)
+ {
+ CompScreen *s;
+ unsigned int i;
+
+- s = findScreenAtDisplay (d, event->root);
++ s = findScreenAtDisplay (d, root);
+ if (!s)
+ return FALSE;
+
+- if (event->window != edgeWindow)
++ if (window != edgeWindow)
+ {
+- if (!s->maxGrab || event->window != s->root)
++ if (!s->maxGrab || window != s->root)
+ return FALSE;
+ }
+
+@@ -296,14 +336,15 @@ triggerButtonPressBindings (CompDisplay *d,
+ {
+ if (isInitiateBinding (option, CompBindingTypeButton, state, &action))
+ {
+- if (action->button.button == event->button)
++ if (action->button.button == button)
+ {
+ bindMods = virtualToRealModMask (d, action->button.modifiers);
+
+- if ((bindMods & modMask) == (event->state & modMask))
++ if ((bindMods & modMask) == (mods & modMask)){
+ if ((*action->initiate) (d, action, state,
+ argument, nArgument))
+ return TRUE;
++ }
+ }
+ }
+
+@@ -312,13 +353,13 @@ triggerButtonPressBindings (CompDisplay *d,
+ if (isInitiateBinding (option, CompBindingTypeEdgeButton,
+ state | CompActionStateInitEdge, &action))
+ {
+- if ((action->button.button == event->button) &&
++ if ((action->button.button == button) &&
+ (action->edgeMask & edge))
+ {
+ bindMods = virtualToRealModMask (d,
+ action->button.modifiers);
+
+- if ((bindMods & modMask) == (event->state & modMask))
++ if ((bindMods & modMask) == (mods & modMask))
+ if ((*action->initiate) (d, action, state |
+ CompActionStateInitEdge,
+ argument, nArgument))
+@@ -329,7 +370,6 @@ triggerButtonPressBindings (CompDisplay *d,
+
+ option++;
+ }
+-
+ return FALSE;
+ }
+
+@@ -337,19 +377,34 @@ static Bool
+ triggerButtonReleaseBindings (CompDisplay *d,
+ CompOption *option,
+ int nOption,
+- XButtonEvent *event,
++ XEvent *event,
+ CompOption *argument,
+ int nArgument)
+ {
+ CompActionState state = CompActionStateTermButton;
+ CompBindingType type = CompBindingTypeButton | CompBindingTypeEdgeButton;
+ CompAction *action;
++ unsigned int button, mods;
++
++ if (event->type == ButtonRelease)
++ {
++ button = event->xbutton.button;
++ mods = event->xbutton.state;
++ } else if (event->type == d->xi_btrelease)
++ {
++ button = ((XDeviceButtonEvent*)event)->button;
++ mods = ((XDeviceButtonEvent*)event)->state;
++ } else
++ {
++ button = 0;
++ mods = 0;
++ }
+
+ while (nOption--)
+ {
+ if (isTerminateBinding (option, type, state, &action))
+ {
+- if (action->button.button == event->button)
++ if (action->button.button == button)
+ {
+ if ((*action->terminate) (d, action, state,
+ argument, nArgument))
+@@ -367,7 +422,7 @@ static Bool
+ triggerKeyPressBindings (CompDisplay *d,
+ CompOption *option,
+ int nOption,
+- XKeyEvent *event,
++ XEvent *event,
+ CompOption *argument,
+ int nArgument)
+ {
+@@ -375,10 +430,25 @@ triggerKeyPressBindings (CompDisplay *d,
+ CompAction *action;
+ unsigned int modMask = REAL_MOD_MASK & ~d->ignoredModMask;
+ unsigned int bindMods;
++ unsigned int keycode, mods;
++
++ if (event->type == KeyPress)
++ {
++ keycode = event->xkey.keycode;
++ mods = event->xkey.state;
++ } else if (event->type == d->xi_kpress)
++ {
++ keycode = ((XDeviceKeyEvent*)event)->keycode;
++ mods = ((XDeviceKeyEvent*)event)->state;
++ } else
++ {
++ keycode = 0;
++ mods = 0;
++ }
+
+- if (event->keycode == d->escapeKeyCode)
++ if (keycode == d->escapeKeyCode)
+ state = CompActionStateCancel;
+- else if (event->keycode == d->returnKeyCode)
++ else if (keycode == d->returnKeyCode)
+ state = CompActionStateCommit;
+
+ if (state)
+@@ -409,16 +479,16 @@ triggerKeyPressBindings (CompDisplay *d,
+ {
+ bindMods = virtualToRealModMask (d, action->key.modifiers);
+
+- if (action->key.keycode == event->keycode)
++ if (action->key.keycode == keycode)
+ {
+- if ((bindMods & modMask) == (event->state & modMask))
++ if ((bindMods & modMask) == (mods & modMask))
+ if ((*action->initiate) (d, action, state,
+ argument, nArgument))
+ return TRUE;
+ }
+ else if (!d->xkbEvent && action->key.keycode == 0)
+ {
+- if (bindMods == (event->state & modMask))
++ if ((bindMods & modMask) == (mods & modMask))
+ if ((*action->initiate) (d, action, state,
+ argument, nArgument))
+ return TRUE;
+@@ -435,19 +505,30 @@ static Bool
+ triggerKeyReleaseBindings (CompDisplay *d,
+ CompOption *option,
+ int nOption,
+- XKeyEvent *event,
++ XEvent *event,
+ CompOption *argument,
+ int nArgument)
+ {
+- CompActionState state = CompActionStateTermKey;
+- CompAction *action;
+- unsigned int modMask = REAL_MOD_MASK & ~d->ignoredModMask;
+- unsigned int bindMods;
+- unsigned int mods;
+
+- mods = keycodeToModifiers (d, event->keycode);
+- if (!d->xkbEvent && !mods)
+- return FALSE;
++ if (!d->xkbEvent)
++ {
++ CompActionState state = CompActionStateTermKey;
++ CompAction *action;
++ unsigned int modMask = REAL_MOD_MASK & ~d->ignoredModMask;
++ unsigned int bindMods;
++ unsigned int mods;
++ unsigned int keycode;
++
++ if (event->type == KeyRelease)
++ keycode = event->xkey.keycode;
++ else if (event->type == d->xi_krelease)
++ keycode = ((XDeviceKeyEvent*)event)->keycode;
++ else
++ keycode = 0;
++
++ mods = keycodeToModifiers (d, keycode);
++ if (mods == 0)
++ return FALSE;
+
+ while (nOption--)
+ {
+@@ -457,7 +538,7 @@ triggerKeyReleaseBindings (CompDisplay *d,
+
+ if ((bindMods & modMask) == 0)
+ {
+- if (action->key.keycode == event->keycode)
++ if (action->key.keycode == keycode)
+ {
+ if ((*action->terminate) (d, action, state,
+ argument, nArgument))
+@@ -474,6 +555,7 @@ triggerKeyReleaseBindings (CompDisplay *d,
+
+ option++;
+ }
++ }
+
+ return FALSE;
+ }
+@@ -815,7 +897,7 @@ handleActionEvent (CompDisplay *d,
+ CompOption *option;
+ int nOption;
+ CompPlugin *p;
+- CompOption o[8];
++ CompOption o[9];
+
+ o[0].type = CompOptionTypeInt;
+ o[0].name = "event_window";
+@@ -835,6 +917,10 @@ handleActionEvent (CompDisplay *d,
+ o[5].type = CompOptionTypeInt;
+ o[5].name = "root";
+
++ o[8].type = CompOptionTypeInt;
++ o[8].name = "device";
++ o[8].value.i = -1;
++
+ switch (event->type) {
+ case ButtonPress:
+ o[0].value.i = event->xbutton.window;
+@@ -859,7 +945,7 @@ handleActionEvent (CompDisplay *d,
+
+ option = (*p->vTable->getObjectOptions) (p, obj, &nOption);
+ if (triggerButtonPressBindings (d, option, nOption,
+- &event->xbutton, o, 8))
++ event, o, 8))
+ return TRUE;
+ }
+ break;
+@@ -886,7 +972,7 @@ handleActionEvent (CompDisplay *d,
+
+ option = (*p->vTable->getObjectOptions) (p, obj, &nOption);
+ if (triggerButtonReleaseBindings (d, option, nOption,
+- &event->xbutton, o, 8))
++ event, o, 8))
+ return TRUE;
+ }
+ break;
+@@ -913,7 +999,7 @@ handleActionEvent (CompDisplay *d,
+
+ option = (*p->vTable->getObjectOptions) (p, obj, &nOption);
+ if (triggerKeyPressBindings (d, option, nOption,
+- &event->xkey, o, 8))
++ event, o, 8))
+ return TRUE;
+ }
+ break;
+@@ -939,7 +1025,7 @@ handleActionEvent (CompDisplay *d,
+ continue;
+ option = (*p->vTable->getObjectOptions) (p, obj, &nOption);
+ if (triggerKeyReleaseBindings (d, option, nOption,
+- &event->xkey, o, 8))
++ event, o, 8))
+ return TRUE;
+ }
+ break;
+@@ -1198,6 +1284,78 @@ handleActionEvent (CompDisplay *d,
+ return TRUE;
+ }
+ }
++ } else if (event->type == d->xi_btpress ||
++ event->type == d->xi_btrelease)
++ {
++ XDeviceButtonEvent *bev = (XDeviceButtonEvent*)event;
++ o[0].value.i = bev->window;
++ o[1].value.i = bev->window;
++ o[2].value.i = bev->state;
++ o[3].value.i = bev->x_root;
++ o[4].value.i = bev->y_root;
++ o[5].value.i = bev->root;
++
++ o[6].type = CompOptionTypeInt;
++ o[6].name = "button";
++ o[6].value.i = bev->button;
++
++ o[7].type = CompOptionTypeInt;
++ o[7].name = "time";
++ o[7].value.i = bev->time;
++
++ o[8].value.i = bev->deviceid;
++
++ for (p = getPlugins (); p; p = p->next)
++ {
++ if (!p->vTable->getObjectOptions)
++ continue;
++
++ option = (*p->vTable->getObjectOptions) (p, obj, &nOption);
++ if (event->type == d->xi_btpress &&
++ triggerButtonPressBindings (d, option, nOption,
++ event, o, 9))
++ return TRUE;
++ else if (event->type == d->xi_btrelease &&
++ triggerButtonReleaseBindings (d, option, nOption,
++ event, o, 9))
++ return TRUE;
++ }
++ } else if (event->type == d->xi_kpress ||
++ event->type == d->xi_krelease)
++ {
++ XDeviceKeyEvent *kev = (XDeviceKeyEvent*)event;
++ o[0].value.i = kev->window;
++ o[1].value.i = d->activeWindow;
++ o[2].value.i = kev->state;
++ o[3].value.i = kev->x_root;
++ o[4].value.i = kev->y_root;
++ o[5].value.i = kev->root;
++
++ o[6].type = CompOptionTypeInt;
++ o[6].name = "keycode";
++ o[6].value.i = kev->keycode;
++
++ o[7].type = CompOptionTypeInt;
++ o[7].name = "time";
++ o[7].value.i = kev->time;
++
++ o[8].value.i = kev->deviceid;
++
++ for (p = getPlugins (); p; p = p->next)
++ {
++ if (!p->vTable->getObjectOptions)
++ continue;
++
++ option = (*p->vTable->getObjectOptions) (p, obj, &nOption);
++ if (event->type == d->xi_kpress &&
++ triggerKeyPressBindings (d, option, nOption, event,
++ o, 9))
++ return TRUE;
++ else if (event->type == d->xi_krelease &&
++ triggerKeyReleaseBindings (d, option, nOption, event,
++ o, 9))
++ return TRUE;
++ }
+ }
+ break;
+ }
+@@ -1247,7 +1405,27 @@ handleEvent (CompDisplay *d,
+ if (handleActionEvent (d, event))
+ {
+ if (!d->screens->maxGrab)
++ {
+ XAllowEvents (d->display, AsyncPointer, event->xbutton.time);
++ if (event->type == d->xi_kpress)
++ {
++ XDeviceKeyEvent *kev = (XDeviceKeyEvent*)event;
++ CompDevice *dev = compFindDeviceById(d, kev->deviceid);
++
++ if (!dev->maxGrab)
++ XAllowDeviceEvents(d->display, dev->dev,
++ ReplayThisDevice, kev->time);
++ }
++ else if (event->type == d->xi_btpress)
++ {
++ XDeviceButtonEvent *bev = (XDeviceButtonEvent*)event;
++ CompDevice *dev = compFindDeviceById(d, bev->deviceid);
++
++ if (!dev->maxGrab)
++ XAllowDeviceEvents(d->display, dev->dev,
++ ReplayThisDevice, bev->time);
++ }
++ }
+
+ return;
+ }
+@@ -1402,9 +1580,31 @@ handleEvent (CompDisplay *d,
+ /* This is the only case where a window is removed but not
+ destroyed. We must remove our event mask and all passive
+ grabs. */
+- XSelectInput (d->display, w->id, NoEventMask);
+- XShapeSelectInput (d->display, w->id, NoEventMask);
+- XUngrabButton (d->display, AnyButton, AnyModifier, w->id);
++
++ if (d->devices)
++ {
++ CompDevice *dev = d->devices;
++ CompDevice *paired;
++ XSelectExtensionEvent(d->display, w->id, NULL, 0);
++ XShapeSelectInput (d->display, w->id, NoEventMask);
++
++ while(dev->id != -1)
++ {
++ if (dev->use == IsXPointer)
++ {
++ paired = compFindDeviceById(d, dev->paired);
++ XUngrabDeviceButton(d->display, dev->dev,
++ AnyButton, AnyModifier,
++ paired->dev, w->id);
++ }
++ dev++;
++ }
++
++ } else {
++ XSelectInput (d->display, w->id, NoEventMask);
++ XShapeSelectInput (d->display, w->id, NoEventMask);
++ XUngrabButton (d->display, AnyButton, AnyModifier, w->id);
++ }
+
+ moveInputFocusToOtherWindow (w);
+
+@@ -2342,6 +2542,7 @@ handleEvent (CompDisplay *d,
+ break;
+ }
+
++
+ if (w)
+ {
+ handleSyncAlarm (w);
+@@ -2350,6 +2551,39 @@ handleEvent (CompDisplay *d,
+ break;
+ }
+ }
++ } else if (event->type == d->xi_btpress)
++ {
++ XDeviceButtonEvent *bev = (XDeviceButtonEvent*)event;
++ CompDevice *dev = compFindDeviceById(d, bev->deviceid);
++
++ s = findScreenAtDisplay (d, bev->root);
++ if (bev->button == Button1 ||
++ bev->button == Button2 ||
++ bev->button == Button3)
++ {
++ w = findTopLevelWindowAtScreen (s, bev->window);
++ if (w)
++ {
++ if (d->opt[COMP_DISPLAY_OPTION_RAISE_ON_CLICK].value.b)
++ updateWindowAttributes (w,
++ CompStackingUpdateModeAboveFullscreen);
++
++ if (!(w->type & CompWindowTypeDockMask))
++ moveInputFocusToWindow (w); /* FIXME: */
++ }
++ }
++
++ if (!dev->maxGrab)
++ XAllowDeviceEvents(d->display, dev->dev,
++ ReplayThisDevice, bev->time);
++ } else if (event->type == d->xi_kpress)
++ {
++ XDeviceKeyEvent *kev = (XDeviceKeyEvent*)event;
++ CompDevice *dev = compFindDeviceById(d, kev->deviceid);
++
++ if (!dev->maxGrab)
++ XAllowDeviceEvents(d->display, dev->dev,
++ ReplayThisDevice, kev->time);
+ }
+ break;
+ }
+diff --git a/src/window.c b/src/window.c
+index 9d80340..afb26b6 100644
+--- a/src/window.c
++++ b/src/window.c
+@@ -2117,9 +2117,26 @@ addWindow (CompScreen *screen,
+
+ w->id = id;
+
+- XGrabButton (d->display, AnyButton, AnyModifier, w->id, TRUE,
+- ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
+- GrabModeSync, GrabModeSync, None, None);
++ if (d->devices)
++ {
++ CompDevice *dev = d->devices;
++ CompDevice *paired;
++ while(dev->id != -1)
++ {
++ if (dev->use == IsXPointer)
++ {
++ paired = compFindDeviceById(d, dev->paired);
++ XGrabDeviceButton(d->display, dev->dev, AnyButton,
++ AnyModifier, paired->dev,
++ w->id, FALSE, 3, &dev->cls_btpress,
++ GrabModeSync, GrabModeSync);
++ }
++ dev++;
++ }
++ } else
++ XGrabButton (d->display, AnyButton, AnyModifier, w->id, TRUE,
++ ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
++ GrabModeSync, GrabModeSync, None, None);
+
+ w->inputHint = TRUE;
+ w->alpha = (w->attrib.depth == 32);
+@@ -2328,7 +2345,23 @@ removeWindow (CompWindow *w)
+
+ XSelectInput (d->display, w->id, NoEventMask);
+
+- XUngrabButton (d->display, AnyButton, AnyModifier, w->id);
++ if (d->devices)
++ {
++ CompDevice *dev = d->devices;
++ CompDevice *paired;
++ while(dev->id != -1)
++ {
++ if (dev->use == IsXPointer)
++ {
++ paired = compFindDeviceById(d, dev->paired);
++ XUngrabDeviceButton(d->display, dev->dev,
++ AnyButton, AnyModifier,
++ paired->dev, w->id);
++ }
++ dev++;
++ }
++ } else
++ XUngrabButton (d->display, AnyButton, AnyModifier, w->id);
+ }
+
+ if (w->attrib.map_state == IsViewable && w->damaged)
+--
+1.5.6
+
diff --git a/compiz/0002-X-Input-2-support-in-plugins.patch b/compiz/0002-X-Input-2-support-in-plugins.patch
new file mode 100644
index 0000000..18b4fd3
--- /dev/null
+++ b/compiz/0002-X-Input-2-support-in-plugins.patch
@@ -0,0 +1,3418 @@
+From 7ef5b707c6297fb8167a6226c8319b83d2432bc1 Mon Sep 17 00:00:00 2001
+From: Sam Spilsbury <Sam@XPS-SUSE.site>
+Date: Fri, 31 Oct 2008 10:28:50 +0900
+Subject: [PATCH] X Input 2 support in plugins
+
+---
+ plugins/move.c | 455 +++++++++++++++++++++-------
+ plugins/resize.c | 880 ++++++++++++++++++++++++++++++++++++++++--------------
+ plugins/scale.c | 445 +++++++++++++++++++++++++--
+ plugins/water.c | 351 ++++++++++++++++++----
+ 4 files changed, 1717 insertions(+), 414 deletions(-)
+
+diff --git a/plugins/move.c b/plugins/move.c
+index 1a4bbda..4c77239 100644
+--- a/plugins/move.c
++++ b/plugins/move.c
+@@ -28,6 +28,7 @@
+ #include <string.h>
+
+ #include <X11/cursorfont.h>
++#include <X11/extensions/XInput.h>
+
+ #include <compiz-core.h>
+
+@@ -61,31 +62,36 @@ static int displayPrivateIndex;
+ #define MOVE_DISPLAY_OPTION_LAZY_POSITIONING 5
+ #define MOVE_DISPLAY_OPTION_NUM 6
+
+-typedef struct _MoveDisplay {
+- int screenPrivateIndex;
+- HandleEventProc handleEvent;
+-
+- CompOption opt[MOVE_DISPLAY_OPTION_NUM];
+-
++typedef struct _MoveWindow {
+ CompWindow *w;
+- int savedX;
+- int savedY;
+- int x;
+- int y;
++ CompDevice *dev;
++ int savedX;
++ int savedY;
++ int x;
++ int y;
+ Region region;
+ int status;
+- KeyCode key[NUM_KEYS];
+
+- int releaseButton;
++ int releaseButton;
++
++ int grabIndex;
++} MoveWindow;
++
++typedef struct _MoveDisplay {
++ int screenPrivateIndex;
++ HandleEventProc handleEvent;
++
++ CompOption opt[MOVE_DISPLAY_OPTION_NUM];
++ KeyCode key[NUM_KEYS];
++ GLushort moveOpacity;
+
+- GLushort moveOpacity;
++ MoveWindow *win; /* list of windows being moved right now */
++ int nwin;
+ } MoveDisplay;
+
+ typedef struct _MoveScreen {
+ PaintWindowProc paintWindow;
+
+- int grabIndex;
+-
+ Cursor moveCursor;
+
+ unsigned int origState;
+@@ -108,6 +114,82 @@ typedef struct _MoveScreen {
+
+ #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
++static MoveWindow*
++moveGetMoveWinByWin(MoveDisplay *md, CompWindow *win)
++{
++ int i;
++ for (i = 0; i < md->nwin; i++)
++ if (md->win[i].w == win)
++ return &md->win[i];
++ return NULL;
++}
++
++static MoveWindow*
++moveGetMoveWinByDev(MoveDisplay *md, CompDevice *dev)
++{
++ int i;
++ for (i = 0; i < md->nwin; i++)
++ if (md->win[i].dev == dev)
++ return &md->win[i];
++ return NULL;;
++}
++
++/* return TRUE if the given window is grabbed by a device other than */
++static Bool
++moveWindowIsGrabbed(MoveDisplay *md, CompWindow *w, CompDevice *dev)
++{
++ int i;
++ for (i = 0; i < md->nwin; i++)
++ if (md->win[i].w == w && md->win[i].dev != dev)
++ return TRUE;
++ return FALSE;
++}
++
++/**
++ * Find and return the device that is closest to the given coordinates and has
++ * the respective mod-mask set.
++ */
++static CompDevice*
++moveFindDeviceAt(CompDisplay *d, CompWindow *w, int xRoot, int yRoot,
++ unsigned int mods)
++{
++ CompDevice *dev = d->devices;
++ CompDevice *closest = NULL;
++ int delta; /* distance of device to xRoot/yRoot */
++
++ Window root, child;
++ int rx, ry, winx, winy;
++ unsigned int mask;
++
++ for (; dev && dev->id != -1; dev++)
++ {
++ if (dev->use != IsXPointer)
++ continue;
++
++ XQueryDevicePointer(d->display, dev->dev, w->screen->root,
++ &root, &child,
++ &rx, &ry, &winx, &winy, &mask);
++ if (mask != mods)
++ continue;
++
++ if (!closest)
++ {
++ closest = dev;
++ delta = abs(xRoot - rx) + abs(yRoot - ry);
++ continue;
++ }
++
++ if (abs(xRoot - rx) + abs(yRoot - ry) < delta)
++ {
++ closest = dev;
++ delta = abs(xRoot - rx) + abs(yRoot - ry);
++ }
++ }
++
++ return closest;
++}
++
++
+ static Bool
+ moveInitiate (CompDisplay *d,
+ CompAction *action,
+@@ -117,6 +199,7 @@ moveInitiate (CompDisplay *d,
+ {
+ CompWindow *w;
+ Window xid;
++ CompDevice *dev;
+
+ MOVE_DISPLAY (d);
+
+@@ -127,7 +210,8 @@ moveInitiate (CompDisplay *d,
+ {
+ XRectangle workArea;
+ unsigned int mods;
+- int x, y, button;
++ int x, y, button, id;
++ MoveWindow *mw;
+
+ MOVE_SCREEN (w->screen);
+
+@@ -140,10 +224,21 @@ moveInitiate (CompDisplay *d,
+
+ button = getIntOptionNamed (option, nOption, "button", -1);
+
++ id = getIntOptionNamed (option, nOption, "device", -1);
++ dev = compFindDeviceById (d, id);
++
+ if (otherScreenGrabExist (w->screen, "move", 0))
+ return FALSE;
+
+- if (md->w)
++ if (otherDeviceGrabExist (dev, "move", 0))
++ return FALSE;
++
++ mw = moveGetMoveWinByDev(md, dev);
++
++ if (mw)
++ return FALSE;
++
++ if (moveWindowIsGrabbed(md, w, dev))
+ return FALSE;
+
+ if (w->type & (CompWindowTypeDesktopMask |
+@@ -157,22 +252,41 @@ moveInitiate (CompDisplay *d,
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+- if (md->region)
++ /* create new move window */
++ md->nwin++;
++ md->win = realloc(md->win, md->nwin * sizeof(MoveWindow));
++ if (!md->win)
++ return FALSE;
++
++ mw = &md->win[md->nwin - 1];
++ memset(mw, 0, sizeof(MoveWindow));
++
++ mw->w = w;
++ mw->dev = dev;
++
++ if (mw->region)
+ {
+- XDestroyRegion (md->region);
+- md->region = NULL;
++ XDestroyRegion (mw->region);
++ mw->region = NULL;
+ }
+
+- md->status = RectangleOut;
++ mw->status = RectangleOut;
+
+- md->savedX = w->serverX;
+- md->savedY = w->serverY;
++ mw->savedX = w->serverX;
++ mw->savedY = w->serverY;
+
+- md->x = 0;
+- md->y = 0;
++ mw->x = 0;
++ mw->y = 0;
+
+- lastPointerX = x;
+- lastPointerY = y;
++ if (dev)
++ {
++ dev->lastPointerX = x;
++ dev->lastPointerY = y;
++ } else
++ {
++ lastPointerX = x;
++ lastPointerY = y;
++ }
+
+ ms->origState = w->state;
+
+@@ -183,14 +297,19 @@ moveInitiate (CompDisplay *d,
+ ms->snapBackY = w->serverY - workArea.y;
+ ms->snapOffY = y - workArea.y;
+
+- if (!ms->grabIndex)
+- ms->grabIndex = pushScreenGrab (w->screen, ms->moveCursor, "move");
++ if (!mw->grabIndex)
++ {
++ if (dev)
++ mw->grabIndex = pushDeviceGrab (w->screen, dev, ms->moveCursor, "move");
++ else
++ mw->grabIndex = pushScreenGrab (w->screen, ms->moveCursor, "move");
++ }
+
+- if (ms->grabIndex)
++ if (mw->grabIndex)
+ {
+- md->w = w;
++ mw->w = w;
+
+- md->releaseButton = button;
++ mw->releaseButton = button;
+
+ (w->screen->windowGrabNotify) (w, x, y, mods,
+ CompWindowGrabMoveMask |
+@@ -207,7 +326,14 @@ moveInitiate (CompDisplay *d,
+ xRoot = w->attrib.x + (w->width / 2);
+ yRoot = w->attrib.y + (w->height / 2);
+
+- warpPointer (w->screen, xRoot - pointerX, yRoot - pointerY);
++ if (dev)
++ warpDevicePointer (w->screen, dev,
++ xRoot - dev->pointerX,
++ yRoot - dev->pointerY);
++ else
++ warpPointer (w->screen,
++ xRoot - pointerX,
++ yRoot - pointerY);
+ }
+
+ if (md->moveOpacity != OPAQUE)
+@@ -225,38 +351,67 @@ moveTerminate (CompDisplay *d,
+ CompOption *option,
+ int nOption)
+ {
++ CompDevice *dev = NULL;
++ MoveWindow *mw = NULL;
+ MOVE_DISPLAY (d);
+
+- if (md->w)
++ if (option)
++ {
++ int id = getIntOptionNamed(option, nOption, "device", -1);
++ dev = compFindDeviceById(d, id);
++ mw = moveGetMoveWinByDev(md, dev);
++ } else
++ {
++ CompWindow *win;
++ XID wid;
++
++ wid = getIntOptionNamed(option, nOption, "window", 0);
++ win = findWindowAtDisplay(d, wid);
++ mw = moveGetMoveWinByWin(md, win);
++ }
++
++ if (mw && mw->w)
+ {
+- MOVE_SCREEN (md->w->screen);
++ int i;
+
+ if (state & CompActionStateCancel)
+- moveWindow (md->w,
+- md->savedX - md->w->attrib.x,
+- md->savedY - md->w->attrib.y,
++ moveWindow (mw->w,
++ mw->savedX - mw->w->attrib.x,
++ mw->savedY - mw->w->attrib.y,
+ TRUE, FALSE);
+
+- syncWindowPosition (md->w);
++ syncWindowPosition (mw->w);
+
+ /* update window attributes as window constraints may have
+ changed - needed e.g. if a maximized window was moved
+ to another output device */
+- updateWindowAttributes (md->w, CompStackingUpdateModeNone);
++ updateWindowAttributes (mw->w, CompStackingUpdateModeNone);
+
+- (md->w->screen->windowUngrabNotify) (md->w);
++ (mw->w->screen->windowUngrabNotify) (mw->w);
+
+- if (ms->grabIndex)
++ if (mw->grabIndex)
+ {
+- removeScreenGrab (md->w->screen, ms->grabIndex, NULL);
+- ms->grabIndex = 0;
+- }
++ if (dev)
++ removeDeviceGrab (mw->w->screen, dev, mw->grabIndex, NULL);
++ else
++ removeScreenGrab (mw->w->screen, mw->grabIndex, NULL);
++ }
+
+ if (md->moveOpacity != OPAQUE)
+- addWindowDamage (md->w);
++ addWindowDamage (mw->w);
++
++ /* remove window from list */
++ for (i = 0; i < md->nwin - 1; i++)
++ {
++ if (&md->win[i] == mw)
++ {
++ memmove(&md->win[i], &md->win[i + 1],
++ (md->nwin - i - 1) * sizeof(MoveWindow));
++ }
++ }
+
+- md->w = 0;
+- md->releaseButton = 0;
++ md->nwin--;
++ md->win = realloc(md->win, md->nwin * sizeof(MoveWindow));
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+@@ -356,30 +511,34 @@ moveGetYConstrainRegion (CompScreen *s)
+ }
+
+ static void
+-moveHandleMotionEvent (CompScreen *s,
+- int xRoot,
+- int yRoot)
++moveHandleMotionEvent (CompScreen *s,
++ CompDevice *dev,
++ int xRoot,
++ int yRoot)
+ {
++ MoveWindow *mw;
+ MOVE_SCREEN (s);
++ MOVE_DISPLAY (s->display);
++
++ mw = moveGetMoveWinByDev(md, dev);
+
+- if (ms->grabIndex)
++ if (mw && mw->grabIndex)
+ {
+ CompWindow *w;
+ int dx, dy;
+ int wX, wY;
+ int wWidth, wHeight;
+
+- MOVE_DISPLAY (s->display);
+
+- w = md->w;
++ w = mw->w;
+
+ wX = w->serverX;
+ wY = w->serverY;
+ wWidth = w->serverWidth + w->serverBorderWidth * 2;
+ wHeight = w->serverHeight + w->serverBorderWidth * 2;
+
+- md->x += xRoot - lastPointerX;
+- md->y += yRoot - lastPointerY;
++ mw->x += xRoot - ((dev) ? dev->lastPointerX : lastPointerX);
++ mw->y += yRoot - ((dev) ? dev->lastPointerY : lastPointerY);
+
+ if (w->type & CompWindowTypeFullscreenMask)
+ {
+@@ -390,8 +549,8 @@ moveHandleMotionEvent (CompScreen *s,
+ XRectangle workArea;
+ int min, max;
+
+- dx = md->x;
+- dy = md->y;
++ dx = mw->x;
++ dy = mw->y;
+
+ getWorkareaForOutput (s,
+ outputDeviceForWindow (w),
+@@ -399,13 +558,13 @@ moveHandleMotionEvent (CompScreen *s,
+
+ if (md->opt[MOVE_DISPLAY_OPTION_CONSTRAIN_Y].value.b)
+ {
+- if (!md->region)
+- md->region = moveGetYConstrainRegion (s);
++ if (!mw->region)
++ mw->region = moveGetYConstrainRegion (s);
+
+ /* make sure that the top frame extents or the top row of
+ pixels are within what is currently our valid screen
+ region */
+- if (md->region)
++ if (mw->region)
+ {
+ int x, y, width, height;
+ int status;
+@@ -415,16 +574,16 @@ moveHandleMotionEvent (CompScreen *s,
+ width = wWidth + w->input.left + w->input.right;
+ height = w->input.top ? w->input.top : 1;
+
+- status = XRectInRegion (md->region, x, y, width, height);
++ status = XRectInRegion (mw->region, x, y, width, height);
+
+ /* only constrain movement if previous position was valid */
+- if (md->status == RectangleIn)
++ if (mw->status == RectangleIn)
+ {
+ int xStatus = status;
+
+ while (dx && xStatus != RectangleIn)
+ {
+- xStatus = XRectInRegion (md->region,
++ xStatus = XRectInRegion (mw->region,
+ x, y - dy,
+ width, height);
+
+@@ -436,7 +595,7 @@ moveHandleMotionEvent (CompScreen *s,
+
+ while (dy && status != RectangleIn)
+ {
+- status = XRectInRegion (md->region,
++ status = XRectInRegion (mw->region,
+ x, y,
+ width, height);
+
+@@ -448,7 +607,7 @@ moveHandleMotionEvent (CompScreen *s,
+ }
+ else
+ {
+- md->status = status;
++ mw->status = status;
+ }
+ }
+ }
+@@ -471,7 +630,7 @@ moveHandleMotionEvent (CompScreen *s,
+ w->saveWc.x = xRoot - (width >> 1);
+ w->saveWc.y = yRoot + (w->input.top >> 1);
+
+- md->x = md->y = 0;
++ mw->x = mw->y = 0;
+
+ maximizeWindow (w, 0);
+
+@@ -538,10 +697,6 @@ moveHandleMotionEvent (CompScreen *s,
+
+ if (dx || dy)
+ {
+- moveWindow (w,
+- wX + dx - w->attrib.x,
+- wY + dy - w->attrib.y,
+- TRUE, FALSE);
+
+ if (md->opt[MOVE_DISPLAY_OPTION_LAZY_POSITIONING].value.b)
+ {
+@@ -556,8 +711,8 @@ moveHandleMotionEvent (CompScreen *s,
+ syncWindowPosition (w);
+ }
+
+- md->x -= dx;
+- md->y -= dy;
++ mw->x -= dx;
++ mw->y -= dy;
+ }
+ }
+ }
+@@ -567,21 +722,26 @@ moveHandleEvent (CompDisplay *d,
+ XEvent *event)
+ {
+ CompScreen *s;
++ CompWindow *w;
++ CompDevice *dev;
++ MoveWindow *mw;
+
+ MOVE_DISPLAY (d);
+
++
+ switch (event->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+- MOVE_SCREEN (s);
++ w = findWindowAtDisplay(d, event->xbutton.window);
++ mw = moveGetMoveWinByWin(md, w);
+
+- if (ms->grabIndex)
++ if (mw && mw->grabIndex)
+ {
+- if (md->releaseButton == -1 ||
+- md->releaseButton == event->xbutton.button)
++ if (mw->releaseButton == -1 ||
++ mw->releaseButton == event->xbutton.button)
+ {
+ CompAction *action;
+ int opt = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+@@ -597,9 +757,10 @@ moveHandleEvent (CompDisplay *d,
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+- MOVE_SCREEN (s);
++ w = findWindowAtDisplay(d, event->xbutton.window);
++ mw = moveGetMoveWinByWin(md, w);
+
+- if (ms->grabIndex)
++ if (mw && mw->grabIndex)
+ {
+ int i;
+
+@@ -619,13 +780,15 @@ moveHandleEvent (CompDisplay *d,
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+- moveHandleMotionEvent (s, pointerX, pointerY);
++ {
++ moveHandleMotionEvent (s, NULL, pointerX, pointerY);
++ }
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+- moveHandleMotionEvent (s, pointerX, pointerY);
++ moveHandleMotionEvent (s, NULL, pointerX, pointerY);
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->wmMoveResizeAtom)
+@@ -638,7 +801,7 @@ moveHandleEvent (CompDisplay *d,
+ w = findWindowAtDisplay (d, event->xclient.window);
+ if (w)
+ {
+- CompOption o[5];
++ CompOption o[6];
+ int xRoot, yRoot;
+ int option;
+
+@@ -659,15 +822,36 @@ moveHandleEvent (CompDisplay *d,
+ unsigned int mods;
+ Window root, child;
+ int i;
++ CompDevice *dev = NULL;
++ int initMove = FALSE;
+
+ option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+
+- XQueryPointer (d->display, w->screen->root,
+- &root, &child, &xRoot, &yRoot,
+- &i, &i, &mods);
++ if (!d->devices)
++ {
++ XQueryPointer (d->display, w->screen->root,
++ &root, &child, &xRoot, &yRoot,
++ &i, &i, &mods);
++ /* TODO: not only button 1 */
++ initMove = (mods & Button1Mask);
++ } else
++ {
++ /* HACK: ICCCM doesn't give us the ability to
++ * specify a device id so we have to guess. We
++ * simply take the coordinates from the client
++ * message and try to find the device closest to
++ * the coordinates with button 1 down. This is our
++ * candidate. */
++ xRoot = event->xclient.data.l[0];
++ yRoot = event->xclient.data.l[1];
++ dev = moveFindDeviceAt(d, w,
++ xRoot, yRoot,
++ Button1Mask);
++ if (dev)
++ initMove = TRUE;
++ }
+
+- /* TODO: not only button 1 */
+- if (mods & Button1Mask)
++ if (initMove)
+ {
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "modifiers";
+@@ -686,17 +870,22 @@ moveHandleEvent (CompDisplay *d,
+ o[4].value.i = event->xclient.data.l[3] ?
+ event->xclient.data.l[3] : -1;
+
++ o[5].type = CompOptionTypeInt;
++ o[5].name = "device";
++ o[5].value.i = (dev) ? dev->id : -1;
++
+ moveInitiate (d,
+ &md->opt[option].value.action,
+ CompActionStateInitButton,
+- o, 5);
++ o, 6);
+
+- moveHandleMotionEvent (w->screen, xRoot, yRoot);
++ moveHandleMotionEvent (w->screen, NULL,
++ xRoot, yRoot);
+ }
+ }
+ }
+ }
+- else if (md->w && event->xclient.data.l[2] == WmMoveResizeCancel)
++ /*else if (md->w && event->xclient.data.l[2] == WmMoveResizeCancel)
+ {
+ if (md->w->id == event->xclient.window)
+ {
+@@ -711,39 +900,91 @@ moveHandleEvent (CompDisplay *d,
+ &md->opt[option].value.action,
+ CompActionStateCancel, NULL, 0);
+ }
+- }
++ }*/
+ }
+ break;
+ case DestroyNotify:
+- if (md->w && md->w->id == event->xdestroywindow.window)
++ w = findWindowAtDisplay(d, event->xbutton.window);
++ mw = moveGetMoveWinByWin(md, w);
++ if (mw && mw->w && mw->w->id == event->xdestroywindow.window)
+ {
+ int option;
++ CompOption opts[1];
++
++ opts[0].type = CompOptionTypeInt;
++ opts[0].name = "window";
++ opts[0].value.i = event->xdestroywindow.window;
+
+ option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+- 0, NULL, 0);
++ 0, opts, 1);
+ option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+- 0, NULL, 0);
++ 0, opts, 1);
+ }
+ break;
+ case UnmapNotify:
+- if (md->w && md->w->id == event->xunmap.window)
++ w = findWindowAtDisplay(d, event->xbutton.window);
++ mw = moveGetMoveWinByWin(md, w);
++ if (mw && mw->w && mw->w->id == event->xunmap.window)
+ {
+ int option;
++ CompOption opts[1];
++
++ opts[0].type = CompOptionTypeInt;
++ opts[0].name = "window";
++ opts[0].value.i = event->xdestroywindow.window;
+
+ option = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+- 0, NULL, 0);
++ 0, opts, 1);
+ option = MOVE_DISPLAY_OPTION_INITIATE_KEY;
+ moveTerminate (d,
+ &md->opt[option].value.action,
+- 0, NULL, 0);
++ 0, opts, 1);
+ }
+ default:
++ if (event->type == d->xi_motion)
++ {
++ XDeviceMotionEvent* mev = (XDeviceMotionEvent*)event;
++
++ dev = compFindDeviceById(d, mev->deviceid);
++ s = findScreenAtDisplay(d, mev->root);
++ moveHandleMotionEvent(s, dev, dev->pointerX, dev->pointerY);
++ break;
++ }
++
++ if (event->type == d->xi_btrelease || event->type == d->xi_btpress)
++ {
++ XDeviceButtonEvent* bev = (XDeviceButtonEvent*)event;
++
++ dev = compFindDeviceById(d, bev->deviceid);
++ s = findScreenAtDisplay(d, bev->root);
++ mw = moveGetMoveWinByDev(md, dev);
++ if (mw && mw->grabIndex)
++ {
++ if (mw->releaseButton == -1 ||
++ mw->releaseButton == bev->button)
++ {
++ CompAction *action;
++ int opt = MOVE_DISPLAY_OPTION_INITIATE_BUTTON;
++ CompOption option[1];
++
++ option[0].type = CompOptionTypeInt;
++ option[0].name = "device";
++ option[0].value.i = dev->id;
++
++ action = &md->opt[opt].value.action;
++ moveTerminate (d, action, CompActionStateTermButton,
++ option, 1);
++ }
++ }
++ break;
++ }
++
+ break;
+ }
+
+@@ -762,14 +1003,15 @@ movePaintWindow (CompWindow *w,
+ WindowPaintAttrib sAttrib;
+ CompScreen *s = w->screen;
+ Bool status;
++ MoveWindow *mw;
+
+ MOVE_SCREEN (s);
++ MOVE_DISPLAY (s->display);
++ mw = moveGetMoveWinByWin(md, w);
+
+- if (ms->grabIndex)
++ if (mw && mw->grabIndex)
+ {
+- MOVE_DISPLAY (s->display);
+-
+- if (md->w == w && md->moveOpacity != OPAQUE)
++ if (md->moveOpacity != OPAQUE)
+ {
+ /* modify opacity of windows that are not active */
+ sAttrib = *attrib;
+@@ -871,15 +1113,13 @@ moveInitDisplay (CompPlugin *p,
+ md->moveOpacity =
+ (md->opt[MOVE_DISPLAY_OPTION_OPACITY].value.i * OPAQUE) / 100;
+
+- md->w = 0;
+- md->region = NULL;
+- md->status = RectangleOut;
+- md->releaseButton = 0;
+-
+ for (i = 0; i < NUM_KEYS; i++)
+ md->key[i] = XKeysymToKeycode (d->display,
+ XStringToKeysym (mKeys[i].name));
+
++ md->nwin = 0;
++ md->win = NULL;
++
+ WRAP (md, d, handleEvent, moveHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = md;
+@@ -899,6 +1139,7 @@ moveFiniDisplay (CompPlugin *p,
+
+ compFiniDisplayOptions (d, md->opt, MOVE_DISPLAY_OPTION_NUM);
+
++ free (md->win);
+ free (md);
+ }
+
+@@ -914,8 +1155,6 @@ moveInitScreen (CompPlugin *p,
+ if (!ms)
+ return FALSE;
+
+- ms->grabIndex = 0;
+-
+ ms->moveCursor = XCreateFontCursor (s->display->display, XC_fleur);
+
+ WRAP (ms, s, paintWindow, movePaintWindow);
+diff --git a/plugins/resize.c b/plugins/resize.c
+index 47d46eb..75a8569 100644
+--- a/plugins/resize.c
++++ b/plugins/resize.c
+@@ -33,6 +33,7 @@
+ #include <compiz-core.h>
+
+ static CompMetadata resizeMetadata;
++static CompTimeoutHandle resizeTimeoutHandle = -1;
+
+ #define ResizeUpMask (1L << 0)
+ #define ResizeDownMask (1L << 1)
+@@ -80,6 +81,29 @@ struct _ResizeKeys {
+
+ static int displayPrivateIndex;
+
++#define RESIZE_NUM_DIRECTIONS 4
++
++typedef struct _ResizeDirection {
++ CompDevice *dev; /* if NULL, rest is undefined */
++ unsigned int mask;
++ int grabIndex;
++ int releaseButton;
++
++ int pointerDx;
++ int pointerDy;
++} ResizeDirection;
++
++typedef struct _ResizeWindow {
++
++ CompWindow *w;
++ int mode;
++ XRectangle savedGeometry;
++ XRectangle geometry;
++
++ ResizeDirection directions[RESIZE_NUM_DIRECTIONS];
++ int dirty;
++} ResizeWindow;
++
+ typedef struct _ResizeDisplay {
+ CompOption opt[RESIZE_DISPLAY_OPTION_NUM];
+
+@@ -89,21 +113,13 @@ typedef struct _ResizeDisplay {
+ Atom resizeNotifyAtom;
+ Atom resizeInformationAtom;
+
+- CompWindow *w;
+- int mode;
+- XRectangle savedGeometry;
+- XRectangle geometry;
+-
+- int releaseButton;
+- unsigned int mask;
+- int pointerDx;
+- int pointerDy;
+ KeyCode key[NUM_KEYS];
++
++ ResizeWindow *win; /* list of windows being resized right now */
++ int nwin;
+ } ResizeDisplay;
+
+ typedef struct _ResizeScreen {
+- int grabIndex;
+-
+ WindowResizeNotifyProc windowResizeNotify;
+ PaintOutputProc paintOutput;
+ PaintWindowProc paintWindow;
+@@ -135,27 +151,120 @@ typedef struct _ResizeScreen {
+
+ #define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
++static ResizeWindow*
++resizeGetWinByWin(ResizeDisplay *rd, CompWindow *win)
++{
++ int i;
++ for (i = 0; i < rd->nwin; i++)
++ if (rd->win[i].w == win)
++ return &rd->win[i];
++ return NULL;
++}
++
++static ResizeWindow*
++resizeGetWinByDev(ResizeDisplay *rd, CompDevice *dev)
++{
++ int i, j;
++ ResizeDirection *rsd;
++
++ for (i = 0; i < rd->nwin; i++)
++ {
++ for (j = 0; j < RESIZE_NUM_DIRECTIONS; j++)
++ {
++ rsd = &rd->win[i].directions[j];
++ if (rsd->dev == dev)
++ return &rd->win[i];
++ }
++ }
++ return NULL;;
++}
++
++static ResizeDirection*
++resizeGetDirection(ResizeWindow *rw, CompDevice *dev)
++{
++ int i = 0;
++ ResizeDirection *rsd = NULL;
++ for (i = 0; rw && i < RESIZE_NUM_DIRECTIONS; i++)
++ {
++ rsd = &rw->directions[i];
++ if (rsd->dev == dev)
++ return rsd;
++ }
++ return NULL;
++}
++
++/**
++ * Find and return the device that is closest to the given coordinates and has
++ * the respective mod-mask set.
++ */
++static CompDevice*
++resizeFindDeviceAt(CompDisplay *d, CompWindow *w, int xRoot, int yRoot,
++ unsigned int mods)
++{
++ CompDevice *dev = d->devices;
++ CompDevice *closest = NULL;
++ int delta; /* distance of device to xRoot/yRoot */
++
++ Window root, child;
++ int rx, ry, winx, winy;
++ unsigned int mask;
++
++ for (; dev && dev->id != -1; dev++)
++ {
++ if (dev->use != IsXPointer)
++ continue;
++
++ XQueryDevicePointer(d->display, dev->dev, w->screen->root,
++ &root, &child,
++ &rx, &ry, &winx, &winy, &mask);
++ if (mask != mods)
++ continue;
++
++ if (!closest)
++ {
++ closest = dev;
++ delta = abs(xRoot - rx) + abs(yRoot - ry);
++ continue;
++ }
++
++ if (abs(xRoot - rx) + abs(yRoot - ry) < delta)
++ {
++ closest = dev;
++ delta = abs(xRoot - rx) + abs(yRoot - ry);
++ }
++ }
++
++ return closest;
++}
++
+ static void
+ resizeGetPaintRectangle (CompDisplay *d,
++ CompWindow *w,
+ BoxPtr pBox)
+ {
++ ResizeWindow *rw;
+ RESIZE_DISPLAY (d);
+
+- pBox->x1 = rd->geometry.x - rd->w->input.left;
+- pBox->y1 = rd->geometry.y - rd->w->input.top;
+- pBox->x2 = rd->geometry.x +
+- rd->geometry.width + rd->w->serverBorderWidth * 2 +
+- rd->w->input.right;
++ rw = resizeGetWinByWin(rd, w);
++
++ if (!rw)
++ return;
+
+- if (rd->w->shaded)
++ pBox->x1 = rw->geometry.x - rw->w->input.left;
++ pBox->y1 = rw->geometry.y - rw->w->input.top;
++ pBox->x2 = rw->geometry.x +
++ rw->geometry.width + rw->w->serverBorderWidth * 2 +
++ rw->w->input.right;
++
++ if (rw->w->shaded)
+ {
+- pBox->y2 = rd->geometry.y + rd->w->height + rd->w->input.bottom;
++ pBox->y2 = rw->geometry.y + rw->w->height + rw->w->input.bottom;
+ }
+ else
+ {
+- pBox->y2 = rd->geometry.y +
+- rd->geometry.height + rd->w->serverBorderWidth * 2 +
+- rd->w->input.bottom;
++ pBox->y2 = rw->geometry.y +
++ rw->geometry.height + rw->w->serverBorderWidth * 2 +
++ rw->w->input.bottom;
+ }
+ }
+
+@@ -176,20 +285,23 @@ resizeGetStretchScale (CompWindow *w,
+
+ static void
+ resizeGetStretchRectangle (CompDisplay *d,
++ CompWindow *w,
+ BoxPtr pBox)
+ {
+ BoxRec box;
+ float xScale, yScale;
++ ResizeWindow *rw;
+
+ RESIZE_DISPLAY (d);
+
+- resizeGetPaintRectangle (d, &box);
+- resizeGetStretchScale (rd->w, &box, &xScale, &yScale);
++ rw = resizeGetWinByWin(rd, w);
++ resizeGetPaintRectangle (d, w, &box);
++ resizeGetStretchScale (rw->w, &box, &xScale, &yScale);
+
+- pBox->x1 = box.x1 - (rd->w->output.left - rd->w->input.left) * xScale;
+- pBox->y1 = box.y1 - (rd->w->output.top - rd->w->input.top) * yScale;
+- pBox->x2 = box.x2 + rd->w->output.right * xScale;
+- pBox->y2 = box.y2 + rd->w->output.bottom * yScale;
++ pBox->x1 = box.x1 - (rw->w->output.left - rw->w->input.left) * xScale;
++ pBox->y1 = box.y1 - (rw->w->output.top - rw->w->input.top) * yScale;
++ pBox->x2 = box.x2 + rw->w->output.right * xScale;
++ pBox->y2 = box.y2 + rw->w->output.bottom * yScale;
+ }
+
+ static void
+@@ -250,61 +362,104 @@ resizeCursorFromResizeMask (CompScreen *s,
+ }
+
+ static void
+-resizeSendResizeNotify (CompDisplay *d)
++resizeSendResizeNotify (CompDisplay *d, CompWindow *w)
+ {
+ XEvent xev;
++ ResizeWindow *rw;
+
+ RESIZE_DISPLAY (d);
++
++ rw = resizeGetWinByWin(rd, w);
++ if (!rw)
++ return;
++
+ xev.xclient.type = ClientMessage;
+ xev.xclient.display = d->display;
+ xev.xclient.format = 32;
+
+ xev.xclient.message_type = rd->resizeNotifyAtom;
+- xev.xclient.window = rd->w->id;
++ xev.xclient.window = rw->w->id;
+
+- xev.xclient.data.l[0] = rd->geometry.x;
+- xev.xclient.data.l[1] = rd->geometry.y;
+- xev.xclient.data.l[2] = rd->geometry.width;
+- xev.xclient.data.l[3] = rd->geometry.height;
++ xev.xclient.data.l[0] = rw->geometry.x;
++ xev.xclient.data.l[1] = rw->geometry.y;
++ xev.xclient.data.l[2] = rw->geometry.width;
++ xev.xclient.data.l[3] = rw->geometry.height;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent (d->display,
+- rd->w->screen->root,
++ rw->w->screen->root,
+ FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+ }
+
+ static void
+-resizeUpdateWindowProperty (CompDisplay *d)
++resizeUpdateWindowProperty (CompDisplay *d, CompWindow *w)
+ {
+ unsigned long data[4];
++ ResizeWindow *rw;
+
+ RESIZE_DISPLAY (d);
+
+- data[0] = rd->geometry.x;
+- data[1] = rd->geometry.y;
+- data[2] = rd->geometry.width;
+- data[3] = rd->geometry.height;
++ rw = resizeGetWinByWin(rd, w);
++ if (!rw)
++ return;
++
++ data[0] = rw->geometry.x;
++ data[1] = rw->geometry.y;
++ data[2] = rw->geometry.width;
++ data[3] = rw->geometry.height;
+
+- XChangeProperty (d->display, rd->w->id,
++ XChangeProperty (d->display, rw->w->id,
+ rd->resizeInformationAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char*) data, 4);
+ }
+
++/**
++ * Resize all dirty windows. If no dirty window was found, remove this timeout
++ * handler.
++ */
++static CompBool
++resizeTimeout(void *closure)
++{
++ int i, keep = FALSE;
++ ResizeWindow *rw;
++
++ RESIZE_DISPLAY((CompDisplay*)closure);
++
++ for (i = 0; i < rd->nwin; i++)
++ {
++ rw = &rd->win[i];
++ if (rw->dirty)
++ {
++ resizeUpdateWindowProperty(rw->w->screen->display, rw->w);
++ resizeSendResizeNotify (rw->w->screen->display, rw->w);
++ rw->dirty = FALSE;
++ keep = TRUE;
++ }
++ }
++ resizeTimeoutHandle = -1;
++ return keep;
++}
++
++
++
+ static void
+-resizeFinishResizing (CompDisplay *d)
++resizeFinishResizing (CompDisplay *d, CompWindow *w)
+ {
++ ResizeWindow *rw;
+ RESIZE_DISPLAY (d);
+
+- (*rd->w->screen->windowUngrabNotify) (rd->w);
++ rw = resizeGetWinByWin(rd, w);
++
++ (*rw->w->screen->windowUngrabNotify) (rw->w);
+
+ XDeleteProperty (d->display,
+- rd->w->id,
++ rw->w->id,
+ rd->resizeInformationAtom);
+
+- rd->w = NULL;
++ rw->w = NULL;
+ }
+
+ static Bool
+@@ -316,6 +471,7 @@ resizeInitiate (CompDisplay *d,
+ {
+ CompWindow *w;
+ Window xid;
++ CompDevice *dev;
+
+ RESIZE_DISPLAY (d);
+
+@@ -326,18 +482,30 @@ resizeInitiate (CompDisplay *d,
+ {
+ unsigned int mask;
+ int x, y;
+- int button;
++ int button, id;
+ int i;
++ ResizeWindow *rw;
++ ResizeDirection *rsd;
+
+ RESIZE_SCREEN (w->screen);
+
+- x = getIntOptionNamed (option, nOption, "x", pointerX);
+- y = getIntOptionNamed (option, nOption, "y", pointerY);
+-
+ button = getIntOptionNamed (option, nOption, "button", -1);
+
+ mask = getIntOptionNamed (option, nOption, "direction", 0);
+
++ id = getIntOptionNamed (option, nOption, "device", -1);
++ dev = compFindDeviceById(d, id);
++
++ if (dev)
++ {
++ x = getIntOptionNamed (option, nOption, "x", dev->pointerX);
++ y = getIntOptionNamed (option, nOption, "y", dev->pointerY);
++ } else
++ {
++ x = getIntOptionNamed (option, nOption, "x", pointerX);
++ y = getIntOptionNamed (option, nOption, "y", pointerY);
++ }
++
+ /* Initiate the resize in the direction suggested by the
+ * sector of the window the mouse is in, eg drag in top left
+ * will resize up and to the left. Keyboard resize starts out
+@@ -373,7 +541,12 @@ resizeInitiate (CompDisplay *d,
+ if (otherScreenGrabExist (w->screen, "resize", 0))
+ return FALSE;
+
+- if (rd->w)
++ if (otherDeviceGrabExist (dev, "resize", 0))
++ return FALSE;
++
++ rw = resizeGetWinByDev(rd, dev);
++
++ if (rw)
+ return FALSE;
+
+ if (w->type & (CompWindowTypeDesktopMask |
+@@ -390,34 +563,88 @@ resizeInitiate (CompDisplay *d,
+ if (w->shaded)
+ mask &= ~(ResizeUpMask | ResizeDownMask);
+
+- rd->w = w;
+- rd->mask = mask;
++ /* Check if window is being resized with this mask already */
++ for (i = 0; i < rd->nwin; i++)
++ {
++ rw = &rd->win[i];
++ if (rw->w == w)
++ break;
++ rw = NULL; /* urgh. ugly. live with it */
++ }
+
+- rd->savedGeometry.x = w->serverX;
+- rd->savedGeometry.y = w->serverY;
+- rd->savedGeometry.width = w->serverWidth;
+- rd->savedGeometry.height = w->serverHeight;
++ if (rw)
++ {
++ for (i = 0; i < RESIZE_NUM_DIRECTIONS; i++)
++ {
++ rsd = &rw->directions[i];
++ if (rsd->dev && (mask & rsd->mask))
++ return FALSE;
++ }
++ for (i = 0; i < RESIZE_NUM_DIRECTIONS; i++)
++ {
++ rsd = &rw->directions[i];
++ if (!rsd->dev)
++ break;
++ }
++ } else
++ {
++ /* create new resize window */
++ rd->nwin++;
++ rd->win = realloc(rd->win, rd->nwin * sizeof(ResizeWindow));
++ if (!rd->win)
++ return FALSE;
++ rw = &rd->win[rd->nwin - 1];
++ memset(rw, 0, sizeof(ResizeWindow));
++ rw->w = w;
++ rw->savedGeometry.x = w->serverX;
++ rw->savedGeometry.y = w->serverY;
++ rw->savedGeometry.width = w->serverWidth;
++ rw->savedGeometry.height = w->serverHeight;
++ rw->geometry = rw->savedGeometry;
++
++ rsd = &rw->directions[0];
++ }
+
+- rd->geometry = rd->savedGeometry;
++ rsd->dev = dev;
++ rsd->mask = mask;
+
+- rd->pointerDx = x - pointerX;
+- rd->pointerDy = y - pointerY;
++ if (dev)
++ {
++ /* dev->pointerX/Y is not always reliable when a move is
++ * initiated, it's only updated during grabs. So we need to query
++ * instead. */
++ Window root, child;
++ int winx, winy;
++ unsigned int mask;
++ int rx, ry;
++
++ XQueryDevicePointer(d->display, dev->dev,
++ w->id, &root, &child,
++ &rx, &ry, &winx, &winy, &mask);
++
++ rsd->pointerDx = x - rx;
++ rsd->pointerDy = y - ry;
++ } else
++ {
++ rsd->pointerDx = x - pointerX;
++ rsd->pointerDy = y - pointerY;
++ }
+
+ if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
+ {
+ /* if the window is fully maximized, showing the outline or
+ rectangle would be visually distracting as the window can't
+ be resized anyway; so we better don't use them in this case */
+- rd->mode = RESIZE_MODE_NORMAL;
++ rw->mode = RESIZE_MODE_NORMAL;
+ }
+ else
+ {
+- rd->mode = rd->opt[RESIZE_DISPLAY_OPTION_MODE].value.i;
++ rw->mode = rd->opt[RESIZE_DISPLAY_OPTION_MODE].value.i;
+ for (i = 0; i <= RESIZE_MODE_LAST; i++)
+ {
+ if (action == &rd->opt[i].value.action)
+ {
+- rd->mode = i;
++ rw->mode = i;
+ break;
+ }
+ }
+@@ -431,14 +658,14 @@ resizeInitiate (CompDisplay *d,
+ index = RESIZE_DISPLAY_OPTION_NORMAL_MATCH + i;
+ if (matchEval (&rd->opt[index].value.match, w))
+ {
+- rd->mode = i;
++ rw->mode = i;
+ break;
+ }
+ }
+ }
+ }
+
+- if (!rs->grabIndex)
++ if (!rsd->grabIndex)
+ {
+ Cursor cursor;
+
+@@ -451,14 +678,17 @@ resizeInitiate (CompDisplay *d,
+ cursor = resizeCursorFromResizeMask (w->screen, mask);
+ }
+
+- rs->grabIndex = pushScreenGrab (w->screen, cursor, "resize");
++ if (dev)
++ rsd->grabIndex = pushDeviceGrab(w->screen, dev, cursor, "resize");
++ else
++ rsd->grabIndex = pushScreenGrab (w->screen, cursor, "resize");
+ }
+
+- if (rs->grabIndex)
++ if (rsd->grabIndex)
+ {
+ BoxRec box;
+
+- rd->releaseButton = button;
++ rsd->releaseButton = button;
+
+ (w->screen->windowGrabNotify) (w, x, y, state,
+ CompWindowGrabResizeMask |
+@@ -470,7 +700,7 @@ resizeInitiate (CompDisplay *d,
+
+ /* using the paint rectangle is enough here
+ as we don't have any stretch yet */
+- resizeGetPaintRectangle (d, &box);
++ resizeGetPaintRectangle (d, w, &box);
+ resizeDamageRectangle (w->screen, &box);
+
+ if (state & CompActionStateInitKey)
+@@ -480,7 +710,13 @@ resizeInitiate (CompDisplay *d,
+ xRoot = w->serverX + (w->serverWidth / 2);
+ yRoot = w->serverY + (w->serverHeight / 2);
+
+- warpPointer (w->screen, xRoot - pointerX, yRoot - pointerY);
++ if (dev)
++ warpDevicePointer(w->screen, dev,
++ xRoot - dev->pointerX,
++ yRoot - dev->pointerY);
++ else
++ warpPointer (w->screen,
++ xRoot - pointerX, yRoot - pointerY);
+ }
+ }
+ }
+@@ -495,24 +731,43 @@ resizeTerminate (CompDisplay *d,
+ CompOption *option,
+ int nOption)
+ {
++ int i;
++ CompDevice *dev = NULL;
++ ResizeWindow *rw = NULL;
++ ResizeDirection *rsd = NULL;
+ RESIZE_DISPLAY (d);
+
+- if (rd->w)
++ if (option)
++ {
++ int id = getIntOptionNamed(option, nOption, "device", -1);
++ dev = compFindDeviceById(d, id);
++ rw = resizeGetWinByDev(rd, dev);
++ rsd = resizeGetDirection(rw, dev);
++ } else
++ {
++ CompWindow *win;
++ XID wid;
++
++ wid = getIntOptionNamed(option, nOption, "window", 0);
++ win = findWindowAtDisplay(d, wid);
++ rw = resizeGetWinByWin(rd, win);
++ rsd = rw->directions;
++ }
++
++ if (rw && rw->w)
+ {
+- CompWindow *w = rd->w;
++ CompWindow *w = rw->w;
+ XWindowChanges xwc;
+ unsigned int mask = 0;
+
+- RESIZE_SCREEN (w->screen);
+-
+- if (rd->mode == RESIZE_MODE_NORMAL)
++ if (rw->mode == RESIZE_MODE_NORMAL)
+ {
+ if (state & CompActionStateCancel)
+ {
+- xwc.x = rd->savedGeometry.x;
+- xwc.y = rd->savedGeometry.y;
+- xwc.width = rd->savedGeometry.width;
+- xwc.height = rd->savedGeometry.height;
++ xwc.x = rw->savedGeometry.x;
++ xwc.y = rw->savedGeometry.y;
++ xwc.width = rw->savedGeometry.width;
++ xwc.height = rw->savedGeometry.height;
+
+ mask = CWX | CWY | CWWidth | CWHeight;
+ }
+@@ -522,18 +777,18 @@ resizeTerminate (CompDisplay *d,
+ XRectangle geometry;
+
+ if (state & CompActionStateCancel)
+- geometry = rd->savedGeometry;
++ geometry = rw->savedGeometry;
+ else
+- geometry = rd->geometry;
++ geometry = rw->geometry;
+
+- if (memcmp (&geometry, &rd->savedGeometry, sizeof (geometry)) == 0)
++ if (memcmp (&geometry, &rw->savedGeometry, sizeof (geometry)) == 0)
+ {
+ BoxRec box;
+
+- if (rd->mode == RESIZE_MODE_STRETCH)
+- resizeGetStretchRectangle (d, &box);
++ if (rw->mode == RESIZE_MODE_STRETCH)
++ resizeGetStretchRectangle (d, w, &box);
+ else
+- resizeGetPaintRectangle (d, &box);
++ resizeGetPaintRectangle (d, w, &box);
+
+ resizeDamageRectangle (w->screen, &box);
+ }
+@@ -562,16 +817,47 @@ resizeTerminate (CompDisplay *d,
+ configureXWindow (w, mask, &xwc);
+ }
+
+- if (!(mask & (CWWidth | CWHeight)))
+- resizeFinishResizing (d);
+
+- if (rs->grabIndex)
++ if (rsd->grabIndex)
+ {
+- removeScreenGrab (w->screen, rs->grabIndex, NULL);
+- rs->grabIndex = 0;
++ if (dev)
++ removeDeviceGrab (w->screen, dev, rsd->grabIndex, NULL);
++ else
++ removeScreenGrab (w->screen, rsd->grabIndex, NULL);
++ rsd->grabIndex = 0;
+ }
+
+- rd->releaseButton = 0;
++ memset(rsd, 0, sizeof(ResizeDirection));
++
++ /* check if we still have some other resize */
++ for (i = 0; i < RESIZE_NUM_DIRECTIONS; i++)
++ {
++ if (rw->directions[i].dev)
++ break;
++ }
++
++ if (i >= RESIZE_NUM_DIRECTIONS)
++ {
++ if (!(mask & (CWWidth | CWHeight)))
++ resizeFinishResizing (d, w);
++
++ for (i = 0; i < rd->nwin - 1; i++)
++ {
++ if (&rd->win[i] == rw)
++ {
++ memmove(&rd->win[i], &rd->win[i+1],
++ (rd->nwin - i -1) * sizeof(ResizeWindow));
++ }
++ }
++ rd->nwin --;
++ rd->win = realloc(rd->win, rd->nwin * sizeof(ResizeWindow));
++
++ if (resizeTimeoutHandle != -1)
++ {
++ compRemoveTimeout(resizeTimeoutHandle);
++ resizeTimeoutHandle = -1;
++ }
++ }
+ }
+
+ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+@@ -580,26 +866,29 @@ resizeTerminate (CompDisplay *d,
+ }
+
+ static void
+-resizeUpdateWindowSize (CompDisplay *d)
++resizeUpdateWindowSize (CompDisplay *d, CompWindow *w)
+ {
++ ResizeWindow *rw;
+ RESIZE_DISPLAY (d);
+
+- if (rd->w->syncWait)
++ rw = resizeGetWinByWin(rd, w);
++
++ if (!rw || rw->w->syncWait)
+ return;
+
+- if (rd->w->serverWidth != rd->geometry.width ||
+- rd->w->serverHeight != rd->geometry.height)
++ if (rw->w->serverWidth != rw->geometry.width ||
++ rw->w->serverHeight != rw->geometry.height)
+ {
+ XWindowChanges xwc;
+
+- xwc.x = rd->geometry.x;
+- xwc.y = rd->geometry.y;
+- xwc.width = rd->geometry.width;
+- xwc.height = rd->geometry.height;
++ xwc.x = rw->geometry.x;
++ xwc.y = rw->geometry.y;
++ xwc.width = rw->geometry.width;
++ xwc.height = rw->geometry.height;
+
+- sendSyncRequest (rd->w);
++ sendSyncRequest (rw->w);
+
+- configureXWindow (rd->w,
++ configureXWindow (rw->w,
+ CWX | CWY | CWWidth | CWHeight,
+ &xwc);
+ }
+@@ -607,14 +896,20 @@ resizeUpdateWindowSize (CompDisplay *d)
+
+ static void
+ resizeHandleKeyEvent (CompScreen *s,
++ CompDevice *dev,
+ KeyCode keycode)
+ {
++ ResizeWindow *rw;
++ ResizeDirection *rsd;
+ RESIZE_SCREEN (s);
+ RESIZE_DISPLAY (s->display);
+
+- if (rs->grabIndex && rd->w)
++ rw = resizeGetWinByDev(rd, dev);
++ rsd = resizeGetDirection(rw, dev);
++
++ if (rw && rw->w && rsd && rsd->grabIndex)
+ {
+- CompWindow *w = rd->w;
++ CompWindow *w = rw->w;
+ int widthInc, heightInc, i;
+
+ widthInc = w->sizeHints.width_inc;
+@@ -631,11 +926,17 @@ resizeHandleKeyEvent (CompScreen *s,
+ if (keycode != rd->key[i])
+ continue;
+
+- if (rd->mask & rKeys[i].warpMask)
++ if (rsd->mask & rKeys[i].warpMask)
+ {
+- XWarpPointer (s->display->display, None, None, 0, 0, 0, 0,
+- rKeys[i].dx * widthInc,
+- rKeys[i].dy * heightInc);
++ if (dev)
++ XWarpDevicePointer (s->display->display, dev->dev,
++ None, None, 0, 0, 0, 0,
++ rKeys[i].dx * widthInc,
++ rKeys[i].dy * heightInc);
++ else
++ XWarpPointer (s->display->display, None, None, 0, 0, 0, 0,
++ rKeys[i].dx * widthInc,
++ rKeys[i].dy * heightInc);
+ }
+ else
+ {
+@@ -649,11 +950,16 @@ resizeHandleKeyEvent (CompScreen *s,
+ x = left + width * (rKeys[i].dx + 1) / 2;
+ y = top + height * (rKeys[i].dy + 1) / 2;
+
+- warpPointer (s, x - pointerX, y - pointerY);
++ if (dev)
++ warpDevicePointer (s, dev,
++ x - dev->pointerX, y - dev->pointerY);
++ else
++ warpPointer (s, x - pointerX, y - pointerY);
+
+- rd->mask = rKeys[i].resizeMask;
++ rsd->mask = rKeys[i].resizeMask;
+
+- updateScreenGrab (s, rs->grabIndex, rs->cursor[i]);
++ if (!dev) /* XXX */
++ updateScreenGrab (s, rsd->grabIndex, rs->cursor[i]);
+ }
+ break;
+ }
+@@ -662,24 +968,35 @@ resizeHandleKeyEvent (CompScreen *s,
+
+ static void
+ resizeHandleMotionEvent (CompScreen *s,
++ CompDevice *dev,
+ int xRoot,
+ int yRoot)
+ {
+- RESIZE_SCREEN (s);
++ ResizeWindow *rw;
++ ResizeDirection *rsd;
++ RESIZE_DISPLAY (s->display);
++
++ rw = resizeGetWinByDev(rd, dev);
++ rsd = resizeGetDirection(rw, dev);
+
+- if (rs->grabIndex)
++ if (rw && rsd && rsd->grabIndex)
+ {
+ BoxRec box;
+ int w, h;
+
+- RESIZE_DISPLAY (s->display);
+-
+- w = rd->savedGeometry.width;
+- h = rd->savedGeometry.height;
+-
+- if (!rd->mask)
++ if (s->display->devices)
++ {
++ w = rw->geometry.width;
++ h = rw->geometry.height;
++ } else
++ {
++ w = rw->savedGeometry.width;
++ h = rw->savedGeometry.height;
++ }
++
++ if (!rsd->mask)
+ {
+- CompWindow *w = rd->w;
++ CompWindow *w = rw->w;
+ int xDist, yDist;
+ int minPointerOffsetX, minPointerOffsetY;
+
+@@ -702,52 +1019,54 @@ resizeHandleMotionEvent (CompScreen *s,
+ if (abs (xDist) > minPointerOffsetX)
+ {
+ if (xDist > 0)
+- rd->mask |= ResizeRightMask;
++ rsd->mask |= ResizeRightMask;
+ else
+- rd->mask |= ResizeLeftMask;
++ rsd->mask |= ResizeLeftMask;
+ }
+
+ if (abs (yDist) > minPointerOffsetY)
+ {
+ if (yDist > 0)
+- rd->mask |= ResizeDownMask;
++ rsd->mask |= ResizeDownMask;
+ else
+- rd->mask |= ResizeUpMask;
++ rsd->mask |= ResizeUpMask;
+ }
+
+ /* if the pointer movement was enough to determine a
+ direction, warp the pointer to the appropriate edge
+ and set the right cursor */
+- if (rd->mask)
++ if (rsd->mask)
+ {
+ Cursor cursor;
+- CompScreen *s = rd->w->screen;
++ CompScreen *s = rw->w->screen;
+ CompAction *action;
+ int pointerAdjustX = 0;
+ int pointerAdjustY = 0;
+ int option = RESIZE_DISPLAY_OPTION_INITIATE_KEY;
+
+- RESIZE_SCREEN (s);
+-
+ action = &rd->opt[option].value.action;
+ action->state |= CompActionStateTermButton;
+
+- if (rd->mask & ResizeRightMask)
++ if (rsd->mask & ResizeRightMask)
+ pointerAdjustX = w->serverX + w->serverWidth +
+ w->input.right - xRoot;
+- else if (rd->mask & ResizeLeftMask)
++ else if (rsd->mask & ResizeLeftMask)
+ pointerAdjustX = w->serverX - w->input.left - xRoot;
+
+- if (rd->mask & ResizeDownMask)
++ if (rsd->mask & ResizeDownMask)
+ pointerAdjustY = w->serverY + w->serverHeight +
+ w->input.bottom - yRoot;
+- else if (rd->mask & ResizeUpMask)
++ else if (rsd->mask & ResizeUpMask)
+ pointerAdjustY = w->serverY - w->input.top - yRoot;
+
+- warpPointer (s, pointerAdjustX, pointerAdjustY);
++ if (dev)
++ warpDevicePointer(s, dev, pointerAdjustX, pointerAdjustY);
++ else
++ warpPointer (s, pointerAdjustX, pointerAdjustY);
+
+- cursor = resizeCursorFromResizeMask (s, rd->mask);
+- updateScreenGrab (s, rs->grabIndex, cursor);
++ cursor = resizeCursorFromResizeMask (s, rsd->mask);
++ if (!dev) /* XXX */
++ updateScreenGrab (s, rsd->grabIndex, cursor);
+ }
+ }
+ else
+@@ -755,63 +1074,72 @@ resizeHandleMotionEvent (CompScreen *s,
+ /* only accumulate pointer movement if a mask is
+ already set as we don't have a use for the
+ difference information otherwise */
+- rd->pointerDx += xRoot - lastPointerX;
+- rd->pointerDy += yRoot - lastPointerY;
++ if (dev)
++ {
++ rsd->pointerDx = xRoot - dev->lastPointerX;
++ rsd->pointerDy = yRoot - dev->lastPointerY;
++ } else {
++ rsd->pointerDx = xRoot - lastPointerX;
++ rsd->pointerDy = yRoot - lastPointerY;
++ }
+ }
+
+- if (rd->mask & ResizeLeftMask)
+- w -= rd->pointerDx;
+- else if (rd->mask & ResizeRightMask)
+- w += rd->pointerDx;
++ if (rsd->mask & ResizeLeftMask)
++ w -= rsd->pointerDx;
++ else if (rsd->mask & ResizeRightMask)
++ w += rsd->pointerDx;
+
+- if (rd->mask & ResizeUpMask)
+- h -= rd->pointerDy;
+- else if (rd->mask & ResizeDownMask)
+- h += rd->pointerDy;
++ if (rsd->mask & ResizeUpMask)
++ h -= rsd->pointerDy;
++ else if (rsd->mask & ResizeDownMask)
++ h += rsd->pointerDy;
+
+- if (rd->w->state & CompWindowStateMaximizedVertMask)
+- h = rd->w->serverHeight;
++ if (rw->w->state & CompWindowStateMaximizedVertMask)
++ h = rw->w->serverHeight;
+
+- if (rd->w->state & CompWindowStateMaximizedHorzMask)
+- w = rd->w->serverWidth;
++ if (rw->w->state & CompWindowStateMaximizedHorzMask)
++ w = rw->w->serverWidth;
+
+- constrainNewWindowSize (rd->w, w, h, &w, &h);
++ constrainNewWindowSize (rw->w, w, h, &w, &h);
+
+- if (rd->mode != RESIZE_MODE_NORMAL)
++ if (rw->mode != RESIZE_MODE_NORMAL)
+ {
+- if (rd->mode == RESIZE_MODE_STRETCH)
+- resizeGetStretchRectangle (s->display, &box);
++ if (rw->mode == RESIZE_MODE_STRETCH)
++ resizeGetStretchRectangle (s->display, rw->w, &box);
+ else
+- resizeGetPaintRectangle (s->display, &box);
++ resizeGetPaintRectangle (s->display, rw->w, &box);
+
+ resizeDamageRectangle (s, &box);
+ }
+
+- if (rd->mask & ResizeLeftMask)
+- rd->geometry.x -= w - rd->geometry.width;
++ if (rsd->mask & ResizeLeftMask)
++ rw->geometry.x -= w - rw->geometry.width;
+
+- if (rd->mask & ResizeUpMask)
+- rd->geometry.y -= h - rd->geometry.height;
++ if (rsd->mask & ResizeUpMask)
++ rw->geometry.y -= h - rw->geometry.height;
+
+- rd->geometry.width = w;
+- rd->geometry.height = h;
++ if (rsd->mask & (ResizeLeftMask | ResizeRightMask))
++ rw->geometry.width = w;
++ if (rsd->mask & (ResizeUpMask | ResizeDownMask))
++ rw->geometry.height = h;
+
+- if (rd->mode != RESIZE_MODE_NORMAL)
++ if (rw->mode != RESIZE_MODE_NORMAL)
+ {
+- if (rd->mode == RESIZE_MODE_STRETCH)
+- resizeGetStretchRectangle (s->display, &box);
++ if (rw->mode == RESIZE_MODE_STRETCH)
++ resizeGetStretchRectangle (s->display, rw->w, &box);
+ else
+- resizeGetPaintRectangle (s->display, &box);
++ resizeGetPaintRectangle (s->display, rw->w, &box);
+
+ resizeDamageRectangle (s, &box);
+ }
+ else
+ {
+- resizeUpdateWindowSize (s->display);
++ resizeUpdateWindowSize (s->display, rw->w);
+ }
+
+- resizeUpdateWindowProperty (s->display);
+- resizeSendResizeNotify (s->display);
++ rw->dirty = TRUE;
++ if (resizeTimeoutHandle == -1)
++ resizeTimeoutHandle = compAddTimeout(0, 0, resizeTimeout, s->display);
+ }
+ }
+
+@@ -819,7 +1147,11 @@ static void
+ resizeHandleEvent (CompDisplay *d,
+ XEvent *event)
+ {
+- CompScreen *s;
++ CompScreen *s;
++ CompDevice *dev = NULL;
++ CompWindow *w;
++ ResizeWindow *rw = NULL;
++ ResizeDirection *rsd = NULL;
+
+ RESIZE_DISPLAY (d);
+
+@@ -827,51 +1159,62 @@ resizeHandleEvent (CompDisplay *d,
+ case KeyPress:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+- resizeHandleKeyEvent (s, event->xkey.keycode);
++ resizeHandleKeyEvent (s, NULL, event->xkey.keycode);
+ break;
+ case ButtonRelease:
++ if (d->devices)
++ break; /* don't care about core events */
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+- RESIZE_SCREEN (s);
++ w = findWindowAtDisplay(d, event->xbutton.window);
++ rw = resizeGetWinByWin(rd, w);
++ rsd = rw->directions;
+
+- if (rs->grabIndex)
++ if (rw && rsd && rsd->grabIndex)
+ {
+- if (rd->releaseButton == -1 ||
+- event->xbutton.button == rd->releaseButton)
++ if (rsd->releaseButton == -1 ||
++ event->xbutton.button == rsd->releaseButton)
+ {
+ int opt = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
+ CompAction *action = &rd->opt[opt].value.action;
++ CompOption o;
++
++ o.type = CompOptionTypeInt;
++ o.name = "window";
++ o.value.i = event->xbutton.window;
+
+ resizeTerminate (d, action, CompActionStateTermButton,
+- NULL, 0);
++ &o, 1);
+ }
+ }
+ }
+ break;
+ case MotionNotify:
++ if (d->devices)
++ break; /* don't care about core events */
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+- resizeHandleMotionEvent (s, pointerX, pointerY);
++ resizeHandleMotionEvent (s, NULL, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
++ if (d->devices)
++ break; /* don't care about core events */
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+- resizeHandleMotionEvent (s, pointerX, pointerY);
++ resizeHandleMotionEvent (s, NULL, pointerX, pointerY);
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->wmMoveResizeAtom)
+ {
+- CompWindow *w;
+-
+ if (event->xclient.data.l[2] <= WmMoveResizeSizeLeft ||
+ event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
+ {
+ w = findWindowAtDisplay (d, event->xclient.window);
+ if (w)
+ {
+- CompOption o[6];
++ CompOption o[7];
+ int option;
+
+ o[0].type = CompOptionTypeInt;
+@@ -901,15 +1244,35 @@ resizeHandleEvent (CompDisplay *d,
+ unsigned int mods;
+ Window root, child;
+ int xRoot, yRoot, i;
++ int initResize = FALSE;
+
+ option = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
+
+- XQueryPointer (d->display, w->screen->root,
+- &root, &child, &xRoot, &yRoot,
+- &i, &i, &mods);
++ if (!d->devices)
++ {
++ XQueryPointer (d->display, w->screen->root,
++ &root, &child, &xRoot, &yRoot,
++ &i, &i, &mods);
++ /* TODO: not only button 1 */
++ initResize = (mods & Button1Mask);
++ } else
++ {
++ /* HACK: ICCCM doesn't give us the ability to
++ * specify a device id so we have to guess. We
++ * simply take the coordinates from the client
++ * message and try to find the device closest to
++ * the coordinates with button 1 down. This is our
++ * candidate. */
++ xRoot = event->xclient.data.l[0];
++ yRoot = event->xclient.data.l[1];
++ dev = resizeFindDeviceAt(d, w,
++ xRoot, yRoot,
++ Button1Mask);
++ if (dev)
++ initResize = TRUE;
++ }
+
+- /* TODO: not only button 1 */
+- if (mods & Button1Mask)
++ if (initResize)
+ {
+ o[1].type = CompOptionTypeInt;
+ o[1].name = "modifiers";
+@@ -932,17 +1295,22 @@ resizeHandleEvent (CompDisplay *d,
+ o[5].value.i = event->xclient.data.l[3] ?
+ event->xclient.data.l[3] : -1;
+
++ o[6].type = CompOptionTypeInt;
++ o[6].name = "device";
++ o[6].value.i = (dev) ? dev->id : -1;
++
+ resizeInitiate (d,
+ &rd->opt[option].value.action,
+ CompActionStateInitButton,
+- o, 6);
++ o, 7);
+
+- resizeHandleMotionEvent (w->screen, xRoot, yRoot);
++ resizeHandleMotionEvent (w->screen, dev,
++ xRoot, yRoot);
+ }
+ }
+ }
+ }
+- else if (rd->w && event->xclient.data.l[2] == WmMoveResizeCancel)
++ /*else if (rd->w && event->xclient.data.l[2] == WmMoveResizeCancel)
+ {
+ if (rd->w->id == event->xclient.window)
+ {
+@@ -955,11 +1323,13 @@ resizeHandleEvent (CompDisplay *d,
+ resizeTerminate (d, &rd->opt[option].value.action,
+ CompActionStateCancel, NULL, 0);
+ }
+- }
++ }*/
+ }
+ break;
+ case DestroyNotify:
+- if (rd->w && rd->w->id == event->xdestroywindow.window)
++ w = findWindowAtDisplay(d, event->xbutton.window);
++ rw = resizeGetWinByWin(rd, w);
++ if (rw && rw->w && rw->w->id == event->xdestroywindow.window)
+ {
+ int option;
+
+@@ -970,7 +1340,9 @@ resizeHandleEvent (CompDisplay *d,
+ }
+ break;
+ case UnmapNotify:
+- if (rd->w && rd->w->id == event->xunmap.window)
++ w = findWindowAtDisplay(d, event->xbutton.window);
++ rw = resizeGetWinByWin(rd, w);
++ if (rw && rw->w && rw->w->id == event->xunmap.window)
+ {
+ int option;
+
+@@ -980,6 +1352,43 @@ resizeHandleEvent (CompDisplay *d,
+ resizeTerminate (d, &rd->opt[option].value.action, 0, NULL, 0);
+ }
+ default:
++ if (event->type == d->xi_motion)
++ {
++ XDeviceMotionEvent* mev = (XDeviceMotionEvent*)event;
++
++ dev = compFindDeviceById(d, mev->deviceid);
++ s = findScreenAtDisplay(d, mev->root);
++ resizeHandleMotionEvent(s, dev, dev->pointerX, dev->pointerY);
++ break;
++ }
++
++ if (event->type == d->xi_btrelease || event->type == d->xi_btpress)
++ {
++ XDeviceButtonEvent* bev = (XDeviceButtonEvent*)event;
++
++ dev = compFindDeviceById(d, bev->deviceid);
++ s = findScreenAtDisplay(d, bev->root);
++ rw = resizeGetWinByDev(rd, dev);
++ rsd = resizeGetDirection(rw, dev);
++ if (rw && rsd && rsd->grabIndex)
++ {
++ if (rsd->releaseButton == -1 ||
++ rsd->releaseButton == bev->button)
++ {
++ int opt = RESIZE_DISPLAY_OPTION_INITIATE_BUTTON;
++ CompAction *action = &rd->opt[opt].value.action;
++ CompOption option[1];
++
++ option[0].type = CompOptionTypeInt;
++ option[0].name = "device";
++ option[0].value.i = dev->id;
++
++ resizeTerminate (d, action, CompActionStateTermButton,
++ option, 1);
++ }
++ }
++ }
++
+ break;
+ }
+
+@@ -987,16 +1396,17 @@ resizeHandleEvent (CompDisplay *d,
+ (*d->handleEvent) (d, event);
+ WRAP (rd, d, handleEvent, resizeHandleEvent);
+
++ /* XXX need to run through all windows here */
+ if (event->type == d->syncEvent + XSyncAlarmNotify)
+ {
+- if (rd->w)
++ if (rw && rw->w)
+ {
+ XSyncAlarmNotifyEvent *sa;
+
+ sa = (XSyncAlarmNotifyEvent *) event;
+
+- if (rd->w->syncAlarm == sa->alarm)
+- resizeUpdateWindowSize (d);
++ if (rw->w->syncAlarm == sa->alarm)
++ resizeUpdateWindowSize (d, w);
+ }
+ }
+ }
+@@ -1008,6 +1418,8 @@ resizeWindowResizeNotify (CompWindow *w,
+ int dwidth,
+ int dheight)
+ {
++ int i;
++ ResizeWindow *rw;
+ RESIZE_DISPLAY (w->screen->display);
+ RESIZE_SCREEN (w->screen);
+
+@@ -1015,12 +1427,22 @@ resizeWindowResizeNotify (CompWindow *w,
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
+ WRAP (rs, w->screen, windowResizeNotify, resizeWindowResizeNotify);
+
+- if (rd->w == w && !rs->grabIndex)
+- resizeFinishResizing (w->screen->display);
++
++ rw = resizeGetWinByWin(rd, w);
++ if (rw && rw->w == w)
++ {
++ for (i = 0; i < RESIZE_NUM_DIRECTIONS; i++)
++ {
++ if (rw->directions[i].dev && rw->directions[i].grabIndex)
++ return;
++ }
++ resizeFinishResizing (w->screen->display, w);
++ }
+ }
+
+ static void
+ resizePaintRectangle (CompScreen *s,
++ CompWindow *w,
+ const ScreenPaintAttrib *sa,
+ const CompTransform *transform,
+ CompOutput *output,
+@@ -1029,8 +1451,11 @@ resizePaintRectangle (CompScreen *s,
+ {
+ BoxRec box;
+ CompTransform sTransform = *transform;
++ ResizeWindow *rw;
+
+- resizeGetPaintRectangle (s->display, &box);
++ RESIZE_DISPLAY (w->screen->display);
++ rw = resizeGetWinByWin(rd, w);
++ resizeGetPaintRectangle (s->display, w, &box);
+
+ glPushMatrix ();
+
+@@ -1074,13 +1499,15 @@ resizePaintOutput (CompScreen *s,
+ unsigned int mask)
+ {
+ Bool status;
++ ResizeWindow *rw = NULL;
+
+ RESIZE_SCREEN (s);
+ RESIZE_DISPLAY (s->display);
+
+- if (rd->w && (s == rd->w->screen))
++ /* XXX */
++ if (rw && rw->w && (s == rw->w->screen))
+ {
+- if (rd->mode == RESIZE_MODE_STRETCH)
++ if (rw->mode == RESIZE_MODE_STRETCH)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+ }
+
+@@ -1088,21 +1515,30 @@ resizePaintOutput (CompScreen *s,
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (rs, s, paintOutput, resizePaintOutput);
+
+- if (status && rd->w && (s == rd->w->screen))
++ if (status)
+ {
++ int i;
+ unsigned short *border, *fill;
+
+- border = rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR].value.c;
+- fill = rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR].value.c;
++ for (i = 0; i < rd->nwin; i++)
++ {
++ rw = &rd->win[i];
++ if (rw->w && (s == rw->w->screen))
++ {
+
+- switch (rd->mode) {
+- case RESIZE_MODE_OUTLINE:
+- resizePaintRectangle (s, sAttrib, transform, output, border, NULL);
+- break;
+- case RESIZE_MODE_RECTANGLE:
+- resizePaintRectangle (s, sAttrib, transform, output, border, fill);
+- default:
+- break;
++ border = rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR].value.c;
++ fill = rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR].value.c;
++
++ switch (rw->mode) {
++ case RESIZE_MODE_OUTLINE:
++ resizePaintRectangle (s, rw->w, sAttrib, transform, output, border, NULL);
++ break;
++ case RESIZE_MODE_RECTANGLE:
++ resizePaintRectangle (s, rw->w, sAttrib, transform, output, border, fill);
++ default:
++ break;
++ }
++ }
+ }
+ }
+
+@@ -1116,13 +1552,16 @@ resizePaintWindow (CompWindow *w,
+ Region region,
+ unsigned int mask)
+ {
+- CompScreen *s = w->screen;
+- Bool status;
++ CompScreen *s = w->screen;
++ Bool status;
++ ResizeWindow *rw;
+
+ RESIZE_SCREEN (s);
+ RESIZE_DISPLAY (s->display);
+
+- if (w == rd->w && rd->mode == RESIZE_MODE_STRETCH)
++ rw = resizeGetWinByWin(rd, w);
++
++ if (rw && w == rw->w && rw->mode == RESIZE_MODE_STRETCH)
+ {
+ FragmentAttrib fragment;
+ CompTransform wTransform = *transform;
+@@ -1143,7 +1582,7 @@ resizePaintWindow (CompWindow *w,
+ if (w->alpha || fragment.opacity != OPAQUE)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+
+- resizeGetPaintRectangle (s->display, &box);
++ resizeGetPaintRectangle (s->display, w, &box);
+ resizeGetStretchScale (w, &box, &xScale, &yScale);
+
+ xOrigin = w->attrib.x - w->input.left;
+@@ -1152,8 +1591,8 @@ resizePaintWindow (CompWindow *w,
+ matrixTranslate (&wTransform, xOrigin, yOrigin, 0.0f);
+ matrixScale (&wTransform, xScale, yScale, 1.0f);
+ matrixTranslate (&wTransform,
+- (rd->geometry.x - w->attrib.x) / xScale - xOrigin,
+- (rd->geometry.y - w->attrib.y) / yScale - yOrigin,
++ (rw->geometry.x - w->attrib.x) / xScale - xOrigin,
++ (rw->geometry.y - w->attrib.y) / yScale - yOrigin,
+ 0.0f);
+
+ glPushMatrix ();
+@@ -1179,16 +1618,19 @@ resizeDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+ {
+- Bool status = FALSE;
++ Bool status = FALSE;
++ ResizeWindow *rw;
+
+ RESIZE_SCREEN (w->screen);
+ RESIZE_DISPLAY (w->screen->display);
+
+- if (w == rd->w && rd->mode == RESIZE_MODE_STRETCH)
++ rw = resizeGetWinByWin(rd, w);
++
++ if (rw && w == rw->w && rw->mode == RESIZE_MODE_STRETCH)
+ {
+ BoxRec box;
+
+- resizeGetStretchRectangle (w->screen->display, &box);
++ resizeGetStretchRectangle (w->screen->display, w, &box);
+ resizeDamageRectangle (w->screen, &box);
+
+ status = TRUE;
+@@ -1277,9 +1719,8 @@ resizeInitDisplay (CompPlugin *p,
+ return FALSE;
+ }
+
+- rd->w = 0;
+-
+- rd->releaseButton = 0;
++ rd->nwin = 0;
++ rd->win = NULL;
+
+ rd->resizeNotifyAtom = XInternAtom (d->display,
+ "_COMPIZ_RESIZE_NOTIFY", 0);
+@@ -1309,6 +1750,7 @@ resizeFiniDisplay (CompPlugin *p,
+
+ compFiniDisplayOptions (d, rd->opt, RESIZE_DISPLAY_OPTION_NUM);
+
++ free (rd->win);
+ free (rd);
+ }
+
+@@ -1324,8 +1766,6 @@ resizeInitScreen (CompPlugin *p,
+ if (!rs)
+ return FALSE;
+
+- rs->grabIndex = 0;
+-
+ rs->leftCursor = XCreateFontCursor (s->display->display, XC_left_side);
+ rs->rightCursor = XCreateFontCursor (s->display->display, XC_right_side);
+ rs->upCursor = XCreateFontCursor (s->display->display,
+diff --git a/plugins/scale.c b/plugins/scale.c
+index 8488c75..40989b2 100644
+--- a/plugins/scale.c
++++ b/plugins/scale.c
+@@ -47,6 +47,13 @@ static int scaleDisplayPrivateIndex;
+
+ #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
++static Bool
++scaleSelectWindowAt (CompScreen *s,
++ CompDevice *dev,
++ int x,
++ int y,
++ Bool moveInputFocus);
++
+ static CompOption *
+ scaleGetScreenOptions (CompPlugin *plugin,
+ CompScreen *screen,
+@@ -143,6 +150,165 @@ isScaleWin (CompWindow *w)
+ return TRUE;
+ }
+
++
++
++/* Device Management */
++
++static Bool
++scaleGrabDevice (CompScreen *s,
++ ScaleDevice *sDev)
++{
++ if (!sDev->grabIndex)
++ {
++ sDev->grabIndex = pushDeviceGrab (s, sDev->dev, None, "scale");
++ }
++ if (sDev->grabIndex)
++ return TRUE;
++ return FALSE;
++}
++
++static Bool
++scaleAddDeviceToList (CompScreen *s,
++ CompDevice *dev)
++{
++ SCALE_SCREEN (s);
++
++ if (!ss->devices)
++ {
++ ss->devices = calloc (1, sizeof(ScaleDevice));
++ if (!ss->devices)
++ return FALSE;
++ ss->devices->dev = dev;
++ ss->devices->selectedWindow = None;
++ ss->devices->hoveredWindow = None;
++ ss->devices->grabIndex = 0;
++ ss->devices->next = NULL;
++ }
++ else
++ {
++ ScaleDevice *run;
++ for (run = ss->devices; run->next; run = run->next); /* Last device in list */
++
++ run->next = calloc (1, sizeof(ScaleDevice));
++ if (!run->next)
++ return FALSE;
++ run->next->dev = dev;
++ run->next->selectedWindow = None;
++ run->next->hoveredWindow = None;
++ run->next->grabIndex = 0;
++ run->next->next = NULL;
++ }
++
++ return TRUE;
++
++}
++
++static Bool
++scaleRemoveDeviceFromList (CompScreen *s,
++ ScaleDevice *dev)
++{
++ SCALE_SCREEN (s);
++ ScaleDevice *run;
++
++ if (!ss->devices)
++ return FALSE;
++
++
++ if (dev == ss->devices)
++ {
++ if (ss->devices->next)
++ ss->devices = ss->devices->next;
++
++ if (dev)
++ free (dev);
++ }
++ else
++ {
++ for (run = ss->devices; run; run = run->next)
++ {
++ if (run == dev)
++ {
++ if (run->next)
++ run = run->next;
++ else
++ run = NULL;
++ if (dev)
++ free (dev);
++ }
++ }
++ }
++
++ return TRUE;
++}
++
++static void
++scaleCheckList (CompScreen *s)
++{
++ SCALE_SCREEN (s);
++ CompDevice *addDev, *ptr;
++ ScaleDevice *removeDev, *run;
++ Bool ok;
++
++ do
++ {
++ addDev = NULL;
++ removeDev = NULL;
++
++ for (ptr = s->display->devices; ptr->id != -1; ptr++)
++ {
++
++ if (ptr->use != IsXPointer)
++ continue;
++
++ if (!ss->devices)
++ addDev = ptr;
++ else
++ for (run = ss->devices; run; run = run->next)
++ {
++ addDev = ptr;
++ if (run->dev == ptr)
++ {
++ addDev = NULL;
++ break;
++ }
++ }
++ if (addDev)
++ break;
++ }
++ for (run = ss->devices; run; run = run->next)
++ {
++ for (ptr = s->display->devices; ptr->id != -1; ptr++)
++ {
++ if (ptr->use != IsXPointer)
++ continue;
++
++ removeDev = run;
++ if (ptr == run->dev)
++ {
++ removeDev = NULL;
++ break;
++ }
++ }
++ if (removeDev)
++ break;
++ }
++
++ if (addDev)
++ scaleAddDeviceToList (s, addDev);
++
++ if (removeDev)
++ scaleRemoveDeviceFromList (s, removeDev);
++
++ if (!addDev && !removeDev)
++ ok = TRUE;
++ else
++ ok = FALSE;
++
++ } while (!ok);
++
++}
++
++
+ static void
+ scaleActivateEvent (CompScreen *s,
+ Bool activating)
+@@ -309,6 +475,8 @@ setScaledPaintAttributes (CompWindow *w,
+ WindowPaintAttrib *attrib)
+ {
+ Bool drawScaled = FALSE;
++ Bool selected = FALSE;
++ ScaleDevice *sDev;
+
+ SCALE_SCREEN (w->screen);
+ SCALE_WINDOW (w);
+@@ -317,7 +485,19 @@ setScaledPaintAttributes (CompWindow *w,
+ {
+ SCALE_DISPLAY (w->screen->display);
+
+- if (w->id != sd->selectedWindow &&
++ if (w->id == sd->selectedWindow)
++ selected = TRUE;
++ else
++ {
++ for (sDev = ss->devices; sDev; sDev = sDev->next)
++ if (w->id == sDev->selectedWindow)
++ {
++ selected = TRUE;
++ break;
++ }
++ }
++
++ if (!selected &&
+ ss->opacity != OPAQUE &&
+ ss->state != SCALE_STATE_IN)
+ {
+@@ -1009,7 +1189,6 @@ sendDndStatusMessage (CompScreen *s,
+
+ XSendEvent (s->display->display, source, FALSE, 0, &xev);
+ }
+-
+ static Bool
+ scaleTerminate (CompDisplay *d,
+ CompAction *action,
+@@ -1018,11 +1197,19 @@ scaleTerminate (CompDisplay *d,
+ int nOption)
+ {
+ CompScreen *s;
++ CompDevice *dev;
++ ScaleDevice *sDev;
+ Window xid;
+- Bool terminate = TRUE;
++ int deviceid;
+
+ SCALE_DISPLAY (d);
+
++ xid = getIntOptionNamed (option, nOption, "root", 0);
++ deviceid = getIntOptionNamed (option, nOption, "device", -1);
++
++ dev = compFindDeviceById (d, deviceid);
++ Bool terminate = TRUE;
++
+ if (action->state & CompActionStateTermKey)
+ if (sd->opt[SCALE_DISPLAY_OPTION_KEY_BINDINGS_TOGGLE].value.b)
+ terminate = FALSE;
+@@ -1037,18 +1224,13 @@ scaleTerminate (CompDisplay *d,
+
+ for (s = d->screens; s; s = s->next)
+ {
+- SCALE_SCREEN (s);
+-
+- if (xid && s->root != xid)
+- continue;
+
+- if (!ss->grab)
+- continue;
++ SCALE_SCREEN (s);
+
+- if (ss->grabIndex)
++ for (sDev = ss->devices; sDev; sDev = sDev->next)
+ {
+- removeScreenGrab (s, ss->grabIndex, 0);
+- ss->grabIndex = 0;
++ removeDeviceGrab (s, sDev->dev, sDev->grabIndex, NULL);
++ sDev->grabIndex = 0;
+ }
+
+ if (ss->dndTarget)
+@@ -1082,9 +1264,25 @@ scaleTerminate (CompDisplay *d,
+ }
+ else if (ss->state != SCALE_STATE_IN)
+ {
+- w = findWindowAtScreen (s, sd->selectedWindow);
+- if (w)
+- (*s->activateWindow) (w);
++ if (dev)
++ {
++ for (sDev = ss->devices; sDev; sDev = sDev->next)
++ if (sDev->dev == dev)
++ break;
++
++ if (sDev)
++ {
++ w = findWindowAtScreen (s, sDev->selectedWindow);
++ if (w)
++ (*s->activateWindow) (w);
++ }
++ }
++ else
++ {
++ w = findWindowAtScreen (s, sd->selectedWindow);
++ if (w)
++ (*s->activateWindow) (w);
++ }
+ }
+
+ ss->state = SCALE_STATE_IN;
+@@ -1142,6 +1340,7 @@ scaleInitiateCommon (CompScreen *s,
+ int nOption)
+ {
+ CompMatch *match;
++ ScaleDevice *sDev;
+
+ SCALE_DISPLAY (s->display);
+ SCALE_SCREEN (s);
+@@ -1171,13 +1370,15 @@ scaleInitiateCommon (CompScreen *s,
+ if (scaleEnsureDndRedirectWindow (s))
+ ss->grab = TRUE;
+ }
+- else if (!ss->grabIndex)
++ scaleCheckList (s);
++
++ for (sDev = ss->devices; sDev; sDev = sDev->next)
+ {
+- ss->grabIndex = pushScreenGrab (s, ss->cursor, "scale");
+- if (ss->grabIndex)
+- ss->grab = TRUE;
++ ss->grab = scaleGrabDevice (s, sDev);
+ }
+
++ ss->grab = TRUE;
++
+ if (ss->grab)
+ {
+ if (!sd->lastActiveNum)
+@@ -1185,8 +1386,8 @@ scaleInitiateCommon (CompScreen *s,
+
+ sd->previousActiveWindow = s->display->activeWindow;
+ sd->lastActiveWindow = s->display->activeWindow;
+- sd->selectedWindow = s->display->activeWindow;
+- sd->hoveredWindow = None;
++ //sd->selectedWindow = s->display->activeWindow;
++ //sd->hoveredWindow = None;
+
+ ss->state = SCALE_STATE_OUT;
+
+@@ -1369,12 +1570,31 @@ scaleInitiateOutput (CompDisplay *d,
+ }
+
+ static void
+-scaleSelectWindow (CompWindow *w)
++scaleSelectWindow (CompWindow *w,
++ ScaleDevice *sDev)
+
+ {
+ SCALE_DISPLAY (w->screen->display);
+
+- if (sd->selectedWindow != w->id)
++ if (sDev)
++ {
++ if (sDev->selectedWindow != w->id)
++ {
++ CompWindow *old, *new;
++
++ old = findWindowAtScreen (w->screen, sDev->selectedWindow);
++ new = findWindowAtScreen (w->screen, w->id);
++
++ sDev->selectedWindow = w->id;
++
++ if (old)
++ addWindowDamage (old);
++
++ if (new)
++ addWindowDamage (new);
++ }
++ }
++ else if (sd->selectedWindow != w->id)
+ {
+ CompWindow *old, *new;
+
+@@ -1393,21 +1613,25 @@ scaleSelectWindow (CompWindow *w)
+
+ static Bool
+ scaleSelectWindowAt (CompScreen *s,
++ CompDevice *dev,
+ int x,
+ int y,
+ Bool moveInputFocus)
+
+ {
+ CompWindow *w;
++ ScaleDevice *sDev = NULL;
+
+ SCALE_DISPLAY (s->display);
++ SCALE_SCREEN (s);
+
+ w = scaleCheckForWindowAt (s, x, y);
+ if (w && isScaleWin (w))
+ {
++
+ SCALE_SCREEN (s);
+
+- (*ss->selectWindow) (w);
++ //(*ss->selectWindow) (w);
+
+ if (moveInputFocus)
+ {
+@@ -1417,18 +1641,47 @@ scaleSelectWindowAt (CompScreen *s,
+ moveInputFocusToWindow (w);
+ }
+
+- sd->hoveredWindow = w->id;
++ if (dev)
++ {
++ for (sDev = ss->devices; sDev; sDev = sDev->next)
++ if (sDev->dev == dev)
++ break;
+
++ if (sDev)
++ {
++ sd->hoveredWindow = w->id;
++ (*ss->selectWindow) (w, sDev);
++ }
++ }
++ else
++ {
++ sd->hoveredWindow = w->id;
++ (*ss->selectWindow) (w, NULL);
++ }
+ return TRUE;
+ }
+
+- sd->hoveredWindow = None;
++ /* No window was hovered, set hovered window to None
++ * and return
++ */
++ if (dev)
++ {
++ for (sDev = ss->devices; sDev; sDev = sDev->next)
++ if (sDev->dev == dev)
++ break;
++
++ if (sDev)
++ sDev->hoveredWindow = None;
++ }
++ else
++ sd->hoveredWindow = None;
+
+ return FALSE;
+ }
+
+ static void
+ scaleMoveFocusWindow (CompScreen *s,
++ CompDevice *dev,
+ int dx,
+ int dy)
+
+@@ -1500,7 +1753,13 @@ scaleMoveFocusWindow (CompScreen *s,
+ SCALE_DISPLAY (s->display);
+ SCALE_SCREEN (s);
+
+- (*ss->selectWindow) (focus);
++ ScaleDevice *sDev;
++
++ for (sDev = ss->devices; sDev; sDev = sDev->next)
++ if (sDev->dev == dev)
++ break;
++
++ (*ss->selectWindow) (focus, sDev);
+
+ sd->lastActiveNum = focus->activeNum;
+ sd->lastActiveWindow = focus->id;
+@@ -1531,7 +1790,7 @@ scaleRelayoutSlots (CompDisplay *d,
+ if (layoutThumbs (s))
+ {
+ ss->state = SCALE_STATE_OUT;
+- scaleMoveFocusWindow (s, 0, 0);
++ scaleMoveFocusWindow (s, NULL, 0, 0);
+ damageScreen (s);
+ }
+ }
+@@ -1637,6 +1896,7 @@ scaleHoverTimeout (void *closure)
+ return FALSE;
+ }
+
++
+ static void
+ scaleHandleEvent (CompDisplay *d,
+ XEvent *event)
+@@ -1657,22 +1917,22 @@ scaleHandleEvent (CompDisplay *d,
+ {
+ if (event->xkey.keycode == sd->leftKeyCode)
+ {
+- scaleMoveFocusWindow (s, -1, 0);
++ scaleMoveFocusWindow (s, NULL, -1, 0);
+ consumeEvent = TRUE;
+ }
+ else if (event->xkey.keycode == sd->rightKeyCode)
+ {
+- scaleMoveFocusWindow (s, 1, 0);
++ scaleMoveFocusWindow (s, NULL, 1, 0);
+ consumeEvent = TRUE;
+ }
+ else if (event->xkey.keycode == sd->upKeyCode)
+ {
+- scaleMoveFocusWindow (s, 0, -1);
++ scaleMoveFocusWindow (s, NULL, 0, -1);
+ consumeEvent = TRUE;
+ }
+ else if (event->xkey.keycode == sd->downKeyCode)
+ {
+- scaleMoveFocusWindow (s, 0, 1);
++ scaleMoveFocusWindow (s, NULL, 0, 1);
+ consumeEvent = TRUE;
+ }
+ }
+@@ -1697,6 +1957,7 @@ scaleHandleEvent (CompDisplay *d,
+ o.value.i = s->root;
+
+ if (scaleSelectWindowAt (s,
++ NULL,
+ event->xbutton.x_root,
+ event->xbutton.y_root,
+ TRUE))
+@@ -1747,6 +2008,7 @@ scaleHandleEvent (CompDisplay *d,
+ focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b;
+
+ scaleSelectWindowAt (s,
++ NULL,
+ event->xmotion.x_root,
+ event->xmotion.y_root,
+ focus);
+@@ -1801,7 +2063,7 @@ scaleHandleEvent (CompDisplay *d,
+ scaleHoverTimeout,
+ s);
+
+- scaleSelectWindowAt (s, x, y, focus);
++ scaleSelectWindowAt (s, NULL, x, y, focus);
+ }
+ else
+ {
+@@ -1845,7 +2107,120 @@ scaleHandleEvent (CompDisplay *d,
+ }
+ }
+ default:
+- break;
++ if (event->type == d->xi_kpress)
++ {
++ XDeviceKeyEvent *kev = (XDeviceKeyEvent *) event;
++ CompScreen *s = findScreenAtDisplay (d, kev->root);
++ CompDevice *dev = compFindDeviceById (d, kev->deviceid);
++ if (s && dev)
++ {
++ SCALE_SCREEN (s);
++
++ if (ss->grab)
++ {
++ if (kev->keycode == sd->leftKeyCode)
++ scaleMoveFocusWindow (s, dev, -1, 0);
++ else if (kev->keycode == sd->rightKeyCode)
++ scaleMoveFocusWindow (s, dev, 1, 0);
++ else if (kev->keycode == sd->upKeyCode)
++ scaleMoveFocusWindow (s, dev, 0, -1);
++ else if (kev->keycode == sd->downKeyCode)
++ scaleMoveFocusWindow (s, dev, 0, 1);
++ }
++ }
++ }
++ else if (event->type == d->xi_btpress)
++ {
++ XDeviceButtonEvent *bev = (XDeviceButtonEvent *) event;
++ if (bev->button == Button1)
++ {
++ CompScreen *s = findScreenAtDisplay (d, bev->root);
++ CompDevice *dev = compFindDeviceById (d, bev->deviceid);
++ if (s)
++ {
++ int option;
++
++ SCALE_SCREEN (s);
++
++ if (ss->grab && ss->state != SCALE_STATE_IN)
++ {
++ int nOption = 0;
++ CompOption o[2];
++
++ o[nOption].type = CompOptionTypeInt;
++ o[nOption].name = "root";
++ o[nOption].value.i = s->root;
++
++ nOption++;
++
++ o[nOption].type = CompOptionTypeInt;
++ o[nOption].name = "device";
++ o[nOption].value.i = dev->id;
++
++ nOption++;
++
++ if (scaleSelectWindowAt (s,
++ dev,
++ bev->x_root,
++ bev->y_root,
++ TRUE))
++ {
++ option = SCALE_DISPLAY_OPTION_INITIATE_EDGE;
++ scaleTerminate (s->display,
++ &sd->opt[option].value.action,
++ 0, o, nOption);
++ option = SCALE_DISPLAY_OPTION_INITIATE_KEY;
++ scaleTerminate (s->display,
++ &sd->opt[option].value.action,
++ 0, o, nOption);
++ }
++ else if (bev->x_root > s->workArea.x &&
++ bev->x_root < (s->workArea.x +
++ s->workArea.width) &&
++ bev->y_root > s->workArea.y &&
++ bev->y_root < (s->workArea.y +
++ s->workArea.height))
++ {
++ if (sd->opt[SCALE_DISPLAY_OPTION_SHOW_DESKTOP].value.b)
++ {
++ option = SCALE_DISPLAY_OPTION_INITIATE_EDGE;
++ scaleTerminate (s->display,
++ &sd->opt[option].value.action,
++ 0, o, nOption);
++ option = SCALE_DISPLAY_OPTION_INITIATE_KEY;
++ scaleTerminate (s->display,
++ &sd->opt[option].value.action,
++ 0, o, nOption);
++ (*s->enterShowDesktopMode) (s);
++ }
++ }
++ }
++ }
++ }
++ }
++ else if (event->type == d->xi_motion)
++ {
++ XDeviceMotionEvent *mev = (XDeviceMotionEvent *) event;
++ CompScreen *s = findScreenAtDisplay (d, mev->root);
++ CompDevice *dev = compFindDeviceById (d, mev->deviceid);
++ if (s)
++ {
++ SCALE_SCREEN (s);
++
++ if (ss->grab && ss->state != SCALE_STATE_IN)
++ {
++ Bool focus;
++
++ focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b;
++
++ scaleSelectWindowAt (s,
++ dev,
++ mev->x_root,
++ mev->y_root,
++ focus);
++ }
++ }
++ }
+ }
+
+ if (!consumeEvent)
+@@ -2089,6 +2464,8 @@ scaleInitScreen (CompPlugin *p,
+ ss->slots = 0;
+ ss->slotsSize = 0;
+
++ ss->devices = 0;
++
+ ss->windows = 0;
+ ss->windowsSize = 0;
+
+diff --git a/plugins/water.c b/plugins/water.c
+index ef2f191..38c8283 100644
+--- a/plugins/water.c
++++ b/plugins/water.c
+@@ -49,6 +49,15 @@ typedef struct _WaterFunction {
+ int unit;
+ } WaterFunction;
+
++typedef struct _WaterDevice {
++ struct _WaterDevice *next;
++ CompDevice *dev;
++
++ int grabIndex;
++ int lastPointerX;
++ int lastPointerY;
++} WaterDevice;
++
+ #define TINDEX(ws, i) (((ws)->tIndex + (i)) % TEXTURE_NUM)
+
+ #define CLAMP(v, min, max) \
+@@ -66,15 +75,17 @@ static int displayPrivateIndex;
+ static int waterLastPointerX = 0;
+ static int waterLastPointerY = 0;
+
+-#define WATER_DISPLAY_OPTION_INITIATE_KEY 0
+-#define WATER_DISPLAY_OPTION_TOGGLE_RAIN_KEY 1
+-#define WATER_DISPLAY_OPTION_TOGGLE_WIPER_KEY 2
+-#define WATER_DISPLAY_OPTION_OFFSET_SCALE 3
+-#define WATER_DISPLAY_OPTION_RAIN_DELAY 4
+-#define WATER_DISPLAY_OPTION_TITLE_WAVE 5
+-#define WATER_DISPLAY_OPTION_POINT 6
+-#define WATER_DISPLAY_OPTION_LINE 7
+-#define WATER_DISPLAY_OPTION_NUM 8
++#define WATER_DISPLAY_OPTION_TOGGLE_KEY 0
++#define WATER_DISPLAY_OPTION_INITIATE_KEY 1
++#define WATER_DISPLAY_OPTION_INITIATE_BUTTON 2
++#define WATER_DISPLAY_OPTION_TOGGLE_RAIN_KEY 3
++#define WATER_DISPLAY_OPTION_TOGGLE_WIPER_KEY 4
++#define WATER_DISPLAY_OPTION_OFFSET_SCALE 5
++#define WATER_DISPLAY_OPTION_RAIN_DELAY 6
++#define WATER_DISPLAY_OPTION_TITLE_WAVE 7
++#define WATER_DISPLAY_OPTION_POINT 8
++#define WATER_DISPLAY_OPTION_LINE 9
++#define WATER_DISPLAY_OPTION_NUM 10
+
+ typedef struct _WaterDisplay {
+ int screenPrivateIndex;
+@@ -93,6 +104,7 @@ typedef struct _WaterScreen {
+
+ int grabIndex;
+ int width, height;
++ Bool active;
+
+ GLuint program;
+ GLuint texture[TEXTURE_NUM];
+@@ -113,11 +125,13 @@ typedef struct _WaterScreen {
+
+ CompTimeoutHandle rainHandle;
+ CompTimeoutHandle wiperHandle;
++ CompTimeoutHandle pollHandle;
+
+ float wiperAngle;
+ float wiperSpeed;
+
+ WaterFunction *bumpMapFunctions;
++ WaterDevice *devices;
+ } WaterScreen;
+
+ #define GET_WATER_DISPLAY(d) \
+@@ -1220,38 +1234,112 @@ waterDonePaintScreen (CompScreen *s)
+
+ static void
+ waterHandleMotionEvent (CompDisplay *d,
+- Window root)
++ WaterDevice *wDev,
++ Window root,
++ int pX,
++ int pY)
+ {
+ CompScreen *s;
+
+ s = findScreenAtDisplay (d, root);
+ if (s)
+ {
+- WATER_SCREEN (s);
++ XPoint p[2];
+
+- if (ws->grabIndex)
++ if (wDev)
+ {
+- XPoint p[2];
++ if (wDev->lastPointerX == 0 || wDev->lastPointerY == 0)
++ {
++ p[0].x = wDev->lastPointerX = pX;
++ p[0].y = wDev->lastPointerY = pY;
++ }
++ else
++ {
++ p[0].x = wDev->lastPointerX;
++ p[0].y = wDev->lastPointerY;
++ }
+
++ p[1].x = wDev->lastPointerX = pX;
++ p[1].y = wDev->lastPointerY = pY;
++ }
++ else
++ {
+ p[0].x = waterLastPointerX;
+ p[0].y = waterLastPointerY;
+
+- p[1].x = waterLastPointerX = pointerX;
+- p[1].y = waterLastPointerY = pointerY;
++ p[1].x = waterLastPointerX = pX;
++ p[1].y = waterLastPointerY = pY;
++ }
+
+- waterVertices (s, GL_LINES, p, 2, 0.2f);
++ waterVertices (s, GL_LINES, p, 2, 0.2f);
+
+- damageScreen (s);
++ damageScreen (s);
++ }
++}
++
++static Bool
++waterUpdateMouse (void *c)
++{
++ CompScreen *s = (CompScreen *) c;
++ CompDevice *dev;
++ WaterDevice *wDev;
++ if (!s) return TRUE;
++
++ WATER_SCREEN (s);
++
++ for (dev = s->display->devices; dev->id != -1; dev++)
++ {
++ unsigned int ui;
++ Window root, child;
++ int xRoot, yRoot, i;
++ for (wDev = ws->devices; wDev; wDev = wDev->next)
++ if (wDev->dev == dev)
++ break;
++
++ if (wDev)
++ if (XQueryDevicePointer (s->display->display, dev->dev, s->root, &root, &child, &xRoot, &yRoot,
++ &i, &i, &ui))
++ waterHandleMotionEvent (s->display, wDev, root, xRoot, yRoot);
++ }
++ return TRUE;
++}
++
++static Bool
++waterTerminateToggle (CompDisplay *d,
++ CompAction *action,
++ CompActionState state,
++ CompOption *option,
++ int nOption)
++{
++ CompScreen *s;
++
++ for (s = d->screens; s; s = s->next)
++ {
++ WATER_SCREEN (s);
++ WaterDevice *dev = ws->devices;
++ WaterDevice *next;
++
++ while (dev)
++ {
++ next = dev->next;
++ dev = next;
+ }
++ ws->devices = NULL;
++
++ compRemoveTimeout (ws->pollHandle);
++
+ }
++
++ return FALSE;
+ }
+
++
+ static Bool
+-waterInitiate (CompDisplay *d,
+- CompAction *action,
+- CompActionState state,
+- CompOption *option,
+- int nOption)
++waterInitiateToggle (CompDisplay *d,
++ CompAction *action,
++ CompActionState state,
++ CompOption *option,
++ int nOption)
+ {
+ CompScreen *s;
+ unsigned int ui;
+@@ -1260,58 +1348,179 @@ waterInitiate (CompDisplay *d,
+
+ for (s = d->screens; s; s = s->next)
+ {
++ CompDevice *dev;
++ WaterDevice *device;
+ WATER_SCREEN (s);
+
+- if (otherScreenGrabExist (s, "water", 0))
+- continue;
++ ws->active = !ws->active;
++ if (!ws->active)
++ return waterTerminateToggle (d, action, state, option, nOption);
+
+- if (!ws->grabIndex)
+- ws->grabIndex = pushScreenGrab (s, None, "water");
+-
+- if (XQueryPointer (d->display, s->root, &root, &child, &xRoot, &yRoot,
+- &i, &i, &ui))
++ for (dev = d->devices; dev->id != -1; dev++)
+ {
+- XPoint p;
+
+- p.x = waterLastPointerX = xRoot;
+- p.y = waterLastPointerY = yRoot;
++ if (dev->use != IsXPointer)
++ continue;
+
+- waterVertices (s, GL_POINTS, &p, 1, 0.8f);
++ if (!ws->devices)
++ {
++ ws->devices = calloc (1, sizeof (WaterDevice));
++ ws->devices->next = NULL;
++ ws->devices->dev = dev;
++ device = ws->devices;
++ }
++ else
++ {
++ WaterDevice *run;
++ for (run = ws->devices; run->next; run = run->next);
++ run->next = calloc (1, sizeof (WaterDevice));
++ run->next->dev = dev;
++ run->next->next = NULL;
++ device = run;
++ }
+
+- damageScreen (s);
++ if (XQueryDevicePointer (d->display, dev->dev, s->root, &root, &child, &xRoot, &yRoot,
++ &i, &i, &ui))
++ {
++
++ XPoint p;
++
++ p.x = device->lastPointerX = xRoot;
++ p.y = device->lastPointerY = yRoot;
++
++ waterVertices (s, GL_POINTS, &p, 1, 0.8f);
++
++ damageScreen (s);
++ }
+ }
++ ws->pollHandle = compAddTimeout (10, 12, waterUpdateMouse, s);
+ }
+
+- if (state & CompActionStateInitButton)
+- action->state |= CompActionStateTermButton;
++ return FALSE;
++}
+
+- if (state & CompActionStateInitKey)
+- action->state |= CompActionStateTermKey;
++static Bool
++waterTerminate (CompDisplay *d,
++ CompAction *action,
++ CompActionState state,
++ CompOption *option,
++ int nOption)
++{
++ CompScreen *s;
++ CompDevice *dev;
++ WaterDevice *wDev;
++ dev = compFindDeviceById (d, getIntOptionNamed (option, nOption, "device", -1));
++ s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption, "root", -1));
+
+- return FALSE;
+-}
++ if (s && dev)
++ {
++ WATER_SCREEN (s);
++ for (wDev = ws->devices; wDev; wDev = wDev->next)
++ if (wDev->dev == dev)
++ break;
+
++ if (wDev)
++ {
++ removeDeviceGrab (s, dev, wDev->grabIndex, NULL);
++ if (wDev == ws->devices)
++ {
++ if (wDev->next)
++ ws->devices = ws->devices->next;
++ else
++ ws->devices = NULL;
++ // free (wDev);
++ }
++ else
++ {
++ WaterDevice *run;
++ for (run = ws->devices; run; run = run->next)
++ if (run == wDev)
++ {
++ if (run->next)
++ run = run->next;
++ else
++ run = NULL;
++ // free (wDev);
++ break;
++ }
++ }
++ }
++ if (!ws->devices)
++ action->state &= ~ (CompActionStateTermKey | CompActionStateTermButton);
++ }
++
++ return TRUE;
++}
+ static Bool
+-waterTerminate (CompDisplay *d,
+- CompAction *action,
+- CompActionState state,
+- CompOption *option,
+- int nOption)
++waterInitiate (CompDisplay *d,
++ CompAction *action,
++ CompActionState state,
++ CompOption *option,
++ int nOption)
+ {
+ CompScreen *s;
++ CompDevice *dev;
++ WaterDevice *wDev;
++ unsigned int ui;
++ Window root, child;
++ int xRoot, yRoot, i;
+
+- for (s = d->screens; s; s = s->next)
++ s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption, "root", -1));
++ dev = compFindDeviceById (d, getIntOptionNamed (option, nOption, "device", -1));
++
++ if (s && dev)
+ {
+ WATER_SCREEN (s);
+
+- if (ws->grabIndex)
++ for (wDev = ws->devices; wDev; wDev = wDev->next)
++ if (wDev->dev == dev)
++ break;
++
++ if (!wDev)
+ {
+- removeScreenGrab (s, ws->grabIndex, 0);
+- ws->grabIndex = 0;
++ if (!ws->devices)
++ {
++ ws->devices = calloc (1, sizeof (WaterDevice));
++ if (!ws->devices)
++ return FALSE;
++ wDev = ws->devices;
++ }
++ else
++ {
++ for (wDev = ws->devices; wDev->next; wDev = wDev->next);
++ wDev->next = calloc (1, sizeof (WaterDevice));
++ if (!wDev->next)
++ return FALSE;
++ wDev = wDev->next;
++ }
++ wDev->next = NULL;
++ wDev->dev = dev;
++ wDev->grabIndex = pushDeviceGrab (s, dev, None, "water");
++
++ }
++ if (XQueryDevicePointer (d->display, dev->dev, s->root, &root, &child, &xRoot, &yRoot,
++ &i, &i, &ui))
++ {
++
++ XPoint p;
++
++ p.x = wDev->lastPointerX = xRoot;
++ p.y = wDev->lastPointerY = yRoot;
++
++ waterVertices (s, GL_POINTS, &p, 1, 0.8f);
++
++ damageScreen (s);
+ }
+ }
+
+- return FALSE;
++
++ if (state & CompActionStateInitButton)
++ action->state |= CompActionStateTermButton;
++
++ if (state & CompActionStateInitKey)
++ action->state |= CompActionStateTermKey;
++
++ return TRUE;
+ }
+
+ static Bool
+@@ -1503,14 +1712,49 @@ waterHandleEvent (CompDisplay *d,
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+- waterHandleMotionEvent (d, event->xcrossing.root);
++ s = findScreenAtDisplay (d, event->xcrossing.root);
++ if (s)
++ {
++ WATER_SCREEN (s);
++ if (ws->grabIndex)
++ waterHandleMotionEvent (d, NULL, event->xcrossing.root, pointerX, pointerY);
++ }
+ break;
+ case MotionNotify:
+- waterHandleMotionEvent (d, event->xmotion.root);
++ s = findScreenAtDisplay (d, event->xmotion.root);
++ if (s)
++ {
++ WATER_SCREEN (s);
++ if (ws->grabIndex)
++ waterHandleMotionEvent (d, NULL, event->xmotion.root, pointerX, pointerY);
++ }
+ default:
+ break;
+ }
+
++ if (event->type == d->xi_motion)
++ {
++ XDeviceMotionEvent *mev = (XDeviceMotionEvent *) event;
++ int deviceID = mev->deviceid;
++ CompDevice *dev = compFindDeviceById (d, deviceID);
++ s = findScreenAtDisplay (d, mev->root);
++ WaterDevice *wDev;
++
++ if (s)
++ {
++ WATER_SCREEN (s);
++ for (wDev = ws->devices; wDev; wDev = wDev->next)
++ if (wDev->dev == dev)
++ break;
++
++
++ if (dev && wDev)
++ if (wDev->grabIndex)
++ waterHandleMotionEvent (d, wDev, mev->root, dev->pointerX, dev->pointerY);
++
++ }
++ }
++
+ UNWRAP (wd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (wd, d, handleEvent, waterHandleEvent);
+@@ -1578,7 +1822,9 @@ waterSetDisplayOption (CompPlugin *plugin,
+ }
+
+ static const CompMetadataOptionInfo waterDisplayOptionInfo[] = {
++ { "toggle_key", "key", 0, waterInitiateToggle, 0 },
+ { "initiate_key", "key", 0, waterInitiate, waterTerminate },
++ { "initiate_button", "button", 0, waterInitiate, waterTerminate },
+ { "toggle_rain_key", "key", 0, waterToggleRain, 0 },
+ { "toggle_wiper_key", "key", 0, waterToggleWiper, 0 },
+ { "offset_scale", "float", "<min>0</min>", 0, 0 },
+@@ -1655,6 +1901,7 @@ waterInitScreen (CompPlugin *p,
+ if (!ws)
+ return FALSE;
+
++ ws->devices = NULL;
+ ws->grabIndex = 0;
+
+ WRAP (ws, s, preparePaintScreen, waterPreparePaintScreen);
+--
+1.5.6
+
diff --git a/compiz/0003-XComposite-Triangular-Co-ordinate-Mesh-Input-Redirec.patch b/compiz/0003-XComposite-Triangular-Co-ordinate-Mesh-Input-Redirec.patch
new file mode 100644
index 0000000..ad1e9f4
--- /dev/null
+++ b/compiz/0003-XComposite-Triangular-Co-ordinate-Mesh-Input-Redirec.patch
@@ -0,0 +1,948 @@
+From 9c864e18462e8d59399f653ad6fe4987ea7850da Mon Sep 17 00:00:00 2001
+From: Sam Spilsbury <Sam@XPS-SUSE.site>
+Date: Fri, 31 Oct 2008 11:38:15 +0900
+Subject: [PATCH] XComposite Triangular Co-ordinate Mesh Input Redirection Support
+
+---
+ include/compiz-core.h | 131 ++++++++++++++------
+ src/display.c | 47 ++++---
+ src/event.c | 324 +++++++++++++++++++++++++++++++++++++++++++++++++
+ src/paint.c | 164 +++++++++++++++++++++++++
+ src/screen.c | 3 +
+ src/window.c | 14 ++
+ 6 files changed, 627 insertions(+), 56 deletions(-)
+
+diff --git a/include/compiz-core.h b/include/compiz-core.h
+index 709f2ac..62038e1 100644
+--- a/include/compiz-core.h
++++ b/include/compiz-core.h
+@@ -48,6 +48,7 @@
+
+ #include <GL/gl.h>
+ #include <GL/glx.h>
++#include <GL/glu.h>
+
+ COMPIZ_BEGIN_DECLS
+
+@@ -81,7 +82,9 @@ typedef struct _CompCursor CompCursor;
+ typedef struct _CompMatch CompMatch;
+ typedef struct _CompOutput CompOutput;
+ typedef struct _CompWalker CompWalker;
++typedef struct _CompMesh CompMesh;
+ typedef struct _CompDevice CompDevice;
++typedef struct _CompTransform CompTransform;
+
+ /* virtual modifiers */
+
+@@ -806,7 +809,10 @@ removeFileWatch (CompFileWatchHandle handle);
+ #define COMP_DISPLAY_OPTION_RUN_TERMINAL_KEY 63
+ #define COMP_DISPLAY_OPTION_PING_DELAY 64
+ #define COMP_DISPLAY_OPTION_EDGE_DELAY 65
+-#define COMP_DISPLAY_OPTION_NUM 66
++#define COMP_DISPLAY_OPTION_MESH_MAX 66
++#define COMP_DISPLAY_OPTION_MESH_MIN 67
++#define COMP_DISPLAY_OPTION_REDIRECT_INPUT 68
++#define COMP_DISPLAY_OPTION_NUM 69
+
+ typedef void (*HandleEventProc) (CompDisplay *display,
+ XEvent *event);
+@@ -1271,6 +1277,13 @@ findCursorAtDisplay (CompDisplay *display);
+
+ /* event.c */
+
++typedef int32_t pixman_fixed_16_16_t;
++typedef pixman_fixed_16_16_t pixman_fixed_t;
++#define pixman_int_to_fixed(i) ((pixman_fixed_t) ((i) << 16))
++#define pixman_fixed_to_int(f) ((int) ((f) >> 16))
++#define xFixedToInt(f) pixman_fixed_to_int(f)
++#define IntToxFixed(i) pixman_int_to_fixed(i)
++
+ typedef struct _CompDelayedEdgeSettings
+ {
+ CompDisplay *d;
+@@ -1282,6 +1295,19 @@ typedef struct _CompDelayedEdgeSettings
+ unsigned int nOption;
+ } CompDelayedEdgeSettings;
+
++struct _CompMesh
++{
++ XTriangle *triangles;
++ int width;
++ int height;
++};
++
++typedef void (*TransformMeshProc) (CompWindow *window,
++ CompTransform *transform,
++ CompMesh *mesh,
++ int meshIter,
++ Bool needsProjection);
++
+ void
+ handleEvent (CompDisplay *display,
+ XEvent *event);
+@@ -1310,6 +1336,32 @@ void
+ clearTargetOutput (CompDisplay *display,
+ unsigned int mask);
+
++void
++transformMesh (CompWindow *w,
++ CompTransform *transform,
++ CompMesh *mesh,
++ int meshIter,
++ Bool needsProjection);
++
++void
++updateMesh (CompWindow *w);
++
++void
++setMeshMin (CompWindow *w,
++ int newMin,
++ Bool override);
++
++void
++setMeshMax (CompWindow *w,
++ int newMax,
++ Bool override);
++
++int
++incrementMesh (CompWindow *w);
++
++void
++decrementMesh (CompWindow *w, int meshIter);
++
+ /* paint.c */
+
+ #define MULTIPLY_USHORT(us1, us2) \
+@@ -1320,9 +1372,9 @@ clearTargetOutput (CompDisplay *display,
+
+ #define DEG2RAD (M_PI / 180.0f)
+
+-typedef struct _CompTransform {
++struct _CompTransform {
+ float m[16];
+-} CompTransform;
++};
+
+ typedef union _CompVector {
+ float v[4];
+@@ -1459,7 +1511,7 @@ struct _CompWalker {
+ #define PAINT_WINDOW_OCCLUSION_DETECTION_MASK (1 << 1)
+
+ /*
+- this flag indicates that the window ist painted with
++ this flag indicates that the window is painted with
+ an offset
+ */
+ #define PAINT_WINDOW_WITH_OFFSET_MASK (1 << 2)
+@@ -2236,6 +2288,8 @@ struct _CompScreen {
+ OutputChangeNotifyProc outputChangeNotify;
+
+ InitWindowWalkerProc initWindowWalker;
++
++ TransformMeshProc transformMesh;
+ };
+
+ #define GET_CORE_SCREEN(object) ((CompScreen *) (object))
+@@ -2695,10 +2749,46 @@ struct _CompWindow {
+ int texCoordSize;
+ int indexCount;
+
++ /* Input mesh parameters */
++
++ int nMesh;
++ int meshMin;
++ int meshMax;
++
+ /* must be set by addWindowGeometry */
+ DrawWindowGeometryProc drawWindowGeometry;
+ };
+
++struct _CompDevice {
++ XDevice *dev;
++ int id;
++ int use;
++ int paired;
++ XEventClass cls_btpress;
++ XEventClass cls_btrelease;
++ XEventClass cls_motion;
++ XEventClass cls_enter;
++ XEventClass cls_leave;
++ XEventClass cls_kpress;
++ XEventClass cls_krelease;
++ XEventClass cls_focusin;
++ XEventClass cls_focusout;
++
++ CompGrab *grabs;
++ int grabSize;
++ int maxGrab;
++
++ CompButtonGrab *buttonGrab;
++ int nButtonGrab;
++ CompKeyGrab *keyGrab;
++ int nKeyGrab;
++
++ int pointerX;
++ int pointerY;
++ int lastPointerX;
++ int lastPointerY;
++};
++
+ #define GET_CORE_WINDOW(object) ((CompWindow *) (object))
+ #define CORE_WINDOW(object) CompWindow *w = GET_CORE_WINDOW (object)
+
+@@ -3507,37 +3597,8 @@ compReadXmlChunkFromMetadataOptionInfo (const CompMetadataOptionInfo *info,
+ char *buffer,
+ int length);
+
++/* XInput device-related stuff */
+ /* devices.c */
+-struct _CompDevice {
+- XDevice *dev;
+- int id;
+- int use;
+- int paired;
+- XEventClass cls_btpress;
+- XEventClass cls_btrelease;
+- XEventClass cls_motion;
+- XEventClass cls_enter;
+- XEventClass cls_leave;
+- XEventClass cls_kpress;
+- XEventClass cls_krelease;
+- XEventClass cls_focusin;
+- XEventClass cls_focusout;
+-
+- CompGrab *grabs;
+- int grabSize;
+- int maxGrab;
+-
+- CompButtonGrab *buttonGrab;
+- int nButtonGrab;
+- CompKeyGrab *keyGrab;
+- int nKeyGrab;
+-
+- int pointerX;
+- int pointerY;
+- int lastPointerX;
+- int lastPointerY;
+-};
+-
+ Bool
+ compInitDeviceList (CompDisplay *display);
+
+@@ -3574,8 +3635,6 @@ otherDeviceGrabExist (CompDevice *dev,
+ void
+ compRegisterXIEvents(CompDisplay *d,
+ Window w);
+-
+-
+ COMPIZ_END_DECLS
+
+ #endif
+diff --git a/src/display.c b/src/display.c
+index 5cd29ad..119ed65 100644
+--- a/src/display.c
++++ b/src/display.c
+@@ -681,7 +681,10 @@ const CompMetadataOptionInfo coreDisplayOptionInfo[COMP_DISPLAY_OPTION_NUM] = {
+ { "command_terminal", "string", 0, 0, 0 },
+ { "run_command_terminal_key", "key", 0, runCommandTerminal, 0 },
+ { "ping_delay", "int", "<min>1000</min>", 0, 0 },
+- { "edge_delay", "int", "<min>0</min>", 0, 0 }
++ { "edge_delay", "int", "<min>0</min>", 0, 0 },
++ { "mesh_max", "int", 0, 0, 0 },
++ { "mesh_min", "int", 0, 0, 0 },
++ { "redirect_input", "bool", 0, 0, 0}
+ };
+
+ CompOption *
+@@ -1476,8 +1479,8 @@ eventLoop (void)
+ CompDisplay *d;
+ CompScreen *s;
+ CompWindow *w;
+- CompTimeout *t;
+ CompDevice *dev;
++ CompTimeout *t;
+ int time, timeToNextRedraw = 0;
+ unsigned int damageMask, mask;
+
+@@ -1585,12 +1588,14 @@ eventLoop (void)
+ compRemoveDevice(d, pev->deviceid);
+ }
+
+- lastPointerX = pointerX;
+- lastPointerY = pointerY;
+- for (dev = d->devices; dev->id != -1; dev++)
++ if (dev)
+ {
+ dev->lastPointerX = dev->pointerX;
+ dev->lastPointerY = dev->pointerY;
++ } else
++ {
++ lastPointerX = pointerX;
++ lastPointerY = pointerY;
+ }
+ }
+ }
+@@ -2065,6 +2070,8 @@ addDisplay (const char *name)
+
+ snprintf (d->displayString, 255, "DISPLAY=%s", DisplayString (dpy));
+
++ compInitDeviceList(d);
++
+ #ifdef DEBUG
+ XSynchronize (dpy, TRUE);
+ #endif
+@@ -2350,8 +2357,6 @@ addDisplay (const char *name)
+
+ addDisplayToCore (d);
+
+- compInitDeviceList (d);
+-
+ /* TODO: bailout properly when objectInitPlugins fails */
+ assert (objectInitPlugins (&d->base));
+
+@@ -2525,19 +2530,21 @@ addDisplay (const char *name)
+ ExposureMask);
+ compRegisterXIEvents(d, XRootWindow(dpy, i));
+ } else
+- XSelectInput (dpy, XRootWindow (dpy, i),
+- SubstructureRedirectMask |
+- SubstructureNotifyMask |
+- StructureNotifyMask |
+- PropertyChangeMask |
+- LeaveWindowMask |
+- EnterWindowMask |
+- KeyPressMask |
+- KeyReleaseMask |
+- ButtonPressMask |
+- ButtonReleaseMask |
+- FocusChangeMask |
+- ExposureMask);
++ {
++ XSelectInput (dpy, XRootWindow (dpy, i),
++ SubstructureRedirectMask |
++ SubstructureNotifyMask |
++ StructureNotifyMask |
++ PropertyChangeMask |
++ LeaveWindowMask |
++ EnterWindowMask |
++ KeyPressMask |
++ KeyReleaseMask |
++ ButtonPressMask |
++ ButtonReleaseMask |
++ FocusChangeMask |
++ ExposureMask);
++ }
+
+ if (compCheckForError (dpy))
+ {
+diff --git a/src/event.c b/src/event.c
+index 4dbcc1b..909b708 100644
+--- a/src/event.c
++++ b/src/event.c
+@@ -2588,3 +2588,327 @@ handleEvent (CompDisplay *d,
+ break;
+ }
+ }
++
++static CompMesh *
++createMesh (CompWindow *win,
++ int offX,
++ int offY,
++ int width,
++ int height)
++{
++ CompMesh *mesh;
++ int i;
++ int w, h;
++ int nTriangle = 0;
++
++ mesh = calloc (1, sizeof (CompMesh));
++ if (!mesh)
++ return FALSE;
++
++ mesh->width = MIN (win->meshMin, width);
++ mesh->height = MIN (win->meshMin, height);
++ mesh->triangles = NULL;
++ mesh->triangles = calloc (1, ((mesh->width * mesh->height) * 2) * sizeof(XTriangle));
++
++ for (h = 0; h < mesh->height; h++)
++ {
++ for (w = 0; w < mesh->width; w++)
++ {
++ for (i = 1; i <= 2; i++)
++ {
++ int x1, x2, y1, y2;
++ x1 = (int) offX + (width / mesh->width) * w;
++ x2 = (int) offX + (width / mesh->width) * (w + 1);
++ y1 = (int) offY + (height / mesh->height) * h;
++ y2 = (int) offY + (height / mesh->height) * (h + 1);
++ switch (i)
++ {
++ case 1: /* Triangle is right-side up (first triangle in rectangle) */
++ mesh->triangles[nTriangle].p1.x = IntToxFixed (x1);
++ mesh->triangles[nTriangle].p1.y = IntToxFixed (y1);
++ mesh->triangles[nTriangle].p2.x = IntToxFixed (x1);
++ mesh->triangles[nTriangle].p2.y = IntToxFixed (y2);
++ mesh->triangles[nTriangle].p3.x = IntToxFixed (x2);
++ mesh->triangles[nTriangle].p3.y = IntToxFixed (y2);
++
++ nTriangle++;
++
++ break;
++ case 2: /* Triangle is right-side up (first triangle in rectangle) */
++ mesh->triangles[nTriangle].p1.x = IntToxFixed (x1);
++ mesh->triangles[nTriangle].p1.y = IntToxFixed (y1);
++ mesh->triangles[nTriangle].p2.x = IntToxFixed (x2);
++ mesh->triangles[nTriangle].p2.y = IntToxFixed (y1);
++ mesh->triangles[nTriangle].p3.x = IntToxFixed (x2);
++ mesh->triangles[nTriangle].p3.y = IntToxFixed (y2);
++
++ nTriangle++;
++
++ break;
++ default:
++ break;
++ }
++ }
++ }
++ }
++
++ return mesh;
++}
++
++static CompMesh *
++getDefaultChildMesh (CompWindow *w)
++{
++ return createMesh (w, 0, 0, w->serverWidth, w->serverHeight);
++}
++
++static CompMesh *
++getDefaultParentMesh (CompWindow *w)
++{
++ return createMesh (w, w->serverX, w->serverY, w->serverWidth, w->serverHeight);
++}
++
++static void
++combineMeshes (CompMesh *combined, CompMesh *child, CompMesh *parent)
++{
++ int iTriangle;
++ int iParent = 0;
++ int iChild = 0;
++ int nTriangles = parent->width * parent->height * 4;
++
++ combined->width = parent->width;
++ combined->height = parent->height;
++ combined->triangles = NULL;
++ combined->triangles = calloc (1, ((combined->width * combined->height) * 4) * sizeof(XTriangle));
++
++ for (iTriangle = 0; iTriangle < nTriangles; )
++ {
++ memcpy(&combined->triangles[iTriangle], &parent->triangles[iParent], sizeof (XTriangle));
++ iParent++;
++ iTriangle++;
++ memcpy(&combined->triangles[iTriangle], &child->triangles[iChild], sizeof (XTriangle));
++ iChild++;
++ iTriangle++;
++ }
++
++ free (child->triangles);
++ free (parent->triangles);
++ free (child);
++ free (parent);
++}
++
++static CompMesh *
++combineMeshArray (CompMesh *combinedArray, int nMesh)
++{
++ CompMesh *total;
++ int i;
++ int iTriangle;
++ int triangleOffset = 0, nTriangles = 0;
++ int totalWidth = 0, totalHeight = 0;
++
++ total = calloc (1, sizeof (CompMesh));
++
++ if (!total)
++ return NULL;
++
++ for (i = 0; i < nMesh; i++)
++ {
++ totalWidth += combinedArray[i].width;
++ totalHeight += combinedArray[i].height;
++ }
++
++ total->width = (int) (totalWidth / nMesh);
++ total->height = (int) (totalHeight / nMesh);
++
++ total->triangles = calloc (1, ((totalWidth * totalHeight * 4) * nMesh * sizeof (XTriangle)));
++ if (!total->triangles)
++ return NULL;
++
++ for (i = 0; i < nMesh; i++)
++ {
++ nTriangles = combinedArray[i].width * combinedArray[i].height * 4;
++ for (iTriangle = 0; iTriangle < nTriangles; iTriangle++)
++ {
++ memcpy(&total->triangles[triangleOffset + iTriangle], &combinedArray[i].triangles[iTriangle], sizeof (XTriangle));
++ }
++ triangleOffset += nTriangles;
++ }
++
++ return total;
++}
++
++static void
++projectVector (CompScreen *s,
++ CompTransform *transform,
++ CompVector *vector)
++{
++ GLdouble resultX, resultY, resultZ;
++
++ matrixMultiplyVector(vector, vector, transform);
++
++ GLint viewport[4]; // Viewport
++ GLdouble modelview[16]; // Modelview Matrix
++ GLdouble projection[16]; // Projection Matrix
++
++ glGetIntegerv (GL_VIEWPORT, viewport);
++ glGetDoublev (GL_MODELVIEW_MATRIX, modelview);
++ glGetDoublev (GL_PROJECTION_MATRIX, projection);
++
++ gluProject (vector->x, vector->y, vector->z,
++ modelview, projection, viewport,
++ &resultX, &resultY, &resultZ);
++
++ /* Y must be negated */
++ resultY = s->height - resultY;
++
++ vector->x = resultX;
++ vector->y = resultY;
++ vector->z = resultZ;
++}
++
++void
++transformMesh (CompWindow *w,
++ CompTransform *transform,
++ CompMesh *parent,
++ int meshIter,
++ Bool needsProjection)
++{
++ int nTriangles = parent->width * parent->height * 2;
++ int i;
++
++ for (i = 0; i < nTriangles; i++)
++ {
++ CompVector t1Point = { .v = { xFixedToInt(parent->triangles[i].p1.x), xFixedToInt(parent->triangles[i].p1.y), 0.0f, 1.0f } };
++ CompVector t2Point = { .v = { xFixedToInt(parent->triangles[i].p2.x), xFixedToInt(parent->triangles[i].p2.y), 0.0f, 1.0f } };
++ CompVector t3Point = { .v = { xFixedToInt(parent->triangles[i].p3.x), xFixedToInt(parent->triangles[i].p3.y), 0.0f, 1.0f } };
++
++ if (needsProjection)
++ projectVector (w->screen, transform, &t1Point);
++ else
++ matrixMultiplyVector(&t1Point, &t1Point, transform);
++
++ parent->triangles[i].p1.x = IntToxFixed((int) t1Point.x);
++ parent->triangles[i].p1.y = IntToxFixed((int) t1Point.y);
++
++ if (needsProjection)
++ projectVector (w->screen, transform, &t2Point);
++ else
++ matrixMultiplyVector(&t2Point, &t2Point, transform);
++
++ parent->triangles[i].p2.x = IntToxFixed((int) t2Point.x);
++ parent->triangles[i].p2.y = IntToxFixed((int) t2Point.y);
++
++ if (needsProjection)
++ projectVector (w->screen, transform, &t3Point);
++ else
++ matrixMultiplyVector(&t3Point, &t3Point, transform);
++
++ parent->triangles[i].p3.x = IntToxFixed((int) t3Point.x);
++ parent->triangles[i].p3.y = IntToxFixed((int) t3Point.y);
++ }
++}
++
++void
++setMeshMin (CompWindow *w,
++ int newMin,
++ Bool override)
++{
++ if (newMin < w->screen->display->opt[COMP_DISPLAY_OPTION_MESH_MIN].value.i)
++ return;
++
++ if (newMin > w->meshMax)
++ return;
++
++ if (newMin < w->meshMin && !override)
++ return;
++
++ w->meshMin = newMin;
++}
++
++void
++setMeshMax (CompWindow *w,
++ int newMax,
++ Bool override)
++{
++ if (newMax > w->screen->display->opt[COMP_DISPLAY_OPTION_MESH_MAX].value.i)
++ return;
++
++ if (newMax < w->meshMin)
++ return;
++
++ if (newMax < w->meshMax && !override)
++ return;
++
++ w->meshMax = newMax;
++}
++
++int
++incrementMesh (CompWindow *w)
++{
++ w->nMesh = w->nMesh + 1;
++
++ return w->nMesh;
++}
++
++void
++decrementMesh (CompWindow *w, int meshIter)
++{
++
++ /* TODO: Need to implement something like screenGrabs */
++
++ w->nMesh = w->nMesh - 1;
++
++ if (w->nMesh < 1)
++ w->nMesh = 1;
++}
++void
++updateMesh (CompWindow *w)
++{
++ CompTransform transform;
++ CompMesh *mesh, *child, combinedArray[w->nMesh], *total;
++ int i;
++ int nTriangles;
++ matrixGetIdentity (&transform);
++
++ if (!w->screen->display->opt[COMP_DISPLAY_OPTION_REDIRECT_INPUT].value.b)
++ return;
++
++ if (w->meshMin < 2 || w->meshMin > w->screen->display->opt[COMP_DISPLAY_OPTION_MESH_MAX].value.i)
++ w->meshMin = w->screen->display->opt[COMP_DISPLAY_OPTION_MESH_MIN].value.i;
++
++ if (w->meshMax < 2 || w->meshMax > w->screen->display->opt[COMP_DISPLAY_OPTION_MESH_MIN].value.i)
++ w->meshMax = w->screen->display->opt[COMP_DISPLAY_OPTION_MESH_MAX].value.i;
++
++ for (i = 0; i < w->nMesh; i++)
++ {
++
++ mesh = getDefaultParentMesh (w);
++
++ if (!mesh || !mesh->triangles)
++ return;
++
++ (*w->screen->transformMesh) (w, &transform, mesh, i, FALSE);
++
++ child = getDefaultChildMesh (w);
++ combineMeshes (&combinedArray[i], child, mesh);
++
++ }
++
++ total = combineMeshArray (combinedArray, w->nMesh);
++
++ if (!total)
++ return;
++
++ nTriangles = total->width * total->height * 4 * w->nMesh;
++
++ XCompositeSetTriangularCoordinateMesh (w->screen->display->display,
++ w->id,
++ total->triangles,
++ nTriangles);
++
++ for (i = 0; i < w->nMesh; i++)
++ free (combinedArray[i].triangles);
++
++ free (total->triangles);
++ free (total);
++
++}
+diff --git a/src/paint.c b/src/paint.c
+index 59be51f..138bd75 100644
+--- a/src/paint.c
++++ b/src/paint.c
+@@ -29,6 +29,7 @@
+
+ #include <compiz-core.h>
+
++
+ ScreenPaintAttrib defaultScreenPaintAttrib = {
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -DEFAULT_Z_CAMERA
+ };
+@@ -407,6 +408,7 @@ paintOutputRegion (CompScreen *screen,
+ }
+ }
+
++
+ if (walk.fini)
+ (*walk.fini) (screen, &walk);
+
+@@ -1245,6 +1247,166 @@ paintWindow (CompWindow *w,
+ return TRUE;
+ }
+
++ /* The window was repainted, we need to recalculate it's input
++ * mesh. The mesh accuracy is taken from core options
++ * TODO: Needs to be an option */
++
++ /* The window is divided up into portions, depending on the mesh
++ * accuracy - createMesh is wrappable so plugins can use the base
++ * mesh and adjust it. Once the mesh is adjusted, it is applied
++ * through X.
++ */
++
++ /* 2 Triangles per portion of the window */
++
++ /* ------------ *
++ * |\ | *
++ * | \ | *
++ * | \ t1 | *
++ * | \ | *
++ * | \ | *
++ * | \ | *
++ * | \ | *
++ * | t2 \ | *
++ * | \ | *
++ * | \| *
++ * ------------ */
++
++ /* Create a parent mesh first */
++
++ /*CompMesh parent;
++ int i;
++ int width, height;
++ int nTriangle = 0;
++
++ parent.width = MIN (32, w->serverWidth);
++ parent.height = MIN (32, w->serverHeight);
++ parent.triangles = NULL;
++ parent.triangles = realloc (parent.triangles, ((parent.width * parent.height) * 2) * sizeof(XTriangle));
++ for (height = 0; height < parent.height; height++)
++ {
++ for (width = 0; width < parent.width; width++)
++ {
++ for (i = 1; i <= 2; i++)
++ {
++ int x1, x2, y1, y2;
++ x1 = (int) w->serverX + (w->serverWidth / parent.width) * width;
++ x2 = (int) w->serverX + (w->serverWidth / parent.width) * (width + 1);
++ y1 = (int) w->serverY + (w->serverHeight / parent.height) * height;
++ y2 = (int) w->serverY + (w->serverHeight / parent.height) * (height + 1);
++ switch (i)
++ {
++ case 1:
++ parent.triangles[nTriangle].p1.x = IntToxFixed (x1);
++ parent.triangles[nTriangle].p1.y = IntToxFixed (y1);
++ parent.triangles[nTriangle].p2.x = IntToxFixed (x1);
++ parent.triangles[nTriangle].p2.y = IntToxFixed (y2);
++ parent.triangles[nTriangle].p3.x = IntToxFixed (x2);
++ parent.triangles[nTriangle].p3.y = IntToxFixed (y2);
++
++ nTriangle++;
++
++ break;
++ case 2:
++ parent.triangles[nTriangle].p1.x = IntToxFixed (x1);
++ parent.triangles[nTriangle].p1.y = IntToxFixed (y1);
++ parent.triangles[nTriangle].p2.x = IntToxFixed (x2);
++ parent.triangles[nTriangle].p2.y = IntToxFixed (y1);
++ parent.triangles[nTriangle].p3.x = IntToxFixed (x2);
++ parent.triangles[nTriangle].p3.y = IntToxFixed (y2);
++
++ nTriangle++;
++
++ break;
++ default:
++ break;
++ }
++ }
++ }
++ }
++
++ (*w->screen->createMesh) (w, &parent);
++
++ CompMesh child;
++ nTriangle = 0;
++
++ child.width = MIN (32, w->serverWidth);
++ child.height = MIN (32, w->serverHeight);
++ child.triangles = NULL;
++ child.triangles = realloc (child.triangles, ((child.width * child.height) * 2) * sizeof(XTriangle));
++ for (height = 0; height < child.height; height++)
++ {
++ for (width = 0; width < child.width; width++)
++ {
++ for (i = 1; i <= 2; i++)
++ {
++ int x1, x2, y1, y2;
++ x1 = (int) (w->serverWidth / child.width) * width;
++ x2 = (int) (w->serverWidth / child.width) * (width + 1);
++ y1 = (int) (w->serverHeight / child.height) * height;
++ y2 = (int) (w->serverHeight / child.height) * (height + 1);
++ switch (i)
++ {
++ case 1:
++ child.triangles[nTriangle].p1.x = IntToxFixed (x1);
++ child.triangles[nTriangle].p1.y = IntToxFixed (y1);
++ child.triangles[nTriangle].p2.x = IntToxFixed (x1);
++ child.triangles[nTriangle].p2.y = IntToxFixed (y2);
++ child.triangles[nTriangle].p3.x = IntToxFixed (x2);
++ child.triangles[nTriangle].p3.y = IntToxFixed (y2);
++
++ nTriangle++;
++
++ break;
++ case 2:
++ child.triangles[nTriangle].p1.x = IntToxFixed (x1);
++ child.triangles[nTriangle].p1.y = IntToxFixed (y1);
++ child.triangles[nTriangle].p2.x = IntToxFixed (x2);
++ child.triangles[nTriangle].p2.y = IntToxFixed (y1);
++ child.triangles[nTriangle].p3.x = IntToxFixed (x2);
++ child.triangles[nTriangle].p3.y = IntToxFixed (y2);
++
++ nTriangle++;
++
++ break;
++ default:
++ break;
++ }
++ }
++ }
++ }
++
++ int iChild = 0, iParent = 0, iTriangle = 0;
++ int nTriangles = child.width * child.height * 4;
++
++ CompMesh combined;
++
++ combined.width = MIN (32, w->serverWidth);
++ combined.height = MIN (32, w->serverHeight);
++ combined.triangles = NULL;
++ combined.triangles = realloc (combined.triangles, ((combined.width * combined.height) * 4) * sizeof(XTriangle));
++
++ for (iTriangle = 0; iTriangle < nTriangles; )
++ {
++ combined.triangles[iTriangle] = parent.triangles[iParent];
++ iParent++;
++ iTriangle++;
++ combined.triangles[iTriangle] = child.triangles[iChild];
++ iChild++;
++ iTriangle++;
++ }
++
++
++ XCompositeSetTriangularCoordinateMesh (w->screen->display->display,
++ w->id,
++ combined.triangles,
++ nTriangles);
++
++
++ free (parent.triangles);
++ free (child.triangles);
++ free (combined.triangles);*/
++
+ if (mask & PAINT_WINDOW_NO_CORE_INSTANCE_MASK)
+ return TRUE;
+
+@@ -1263,5 +1425,7 @@ paintWindow (CompWindow *w,
+ mask & PAINT_WINDOW_WITH_OFFSET_MASK)
+ glPopMatrix ();
+
++
++
+ return status;
+ }
+diff --git a/src/screen.c b/src/screen.c
+index b9d7e43..0c3866b 100644
+--- a/src/screen.c
++++ b/src/screen.c
+@@ -1793,6 +1793,8 @@ addScreen (CompDisplay *display,
+
+ s->initWindowWalker = initWindowWalker;
+
++ s->transformMesh = transformMesh;
++
+ s->getProcAddress = 0;
+
+ if (!XGetWindowAttributes (dpy, s->root, &s->attrib))
+@@ -2687,6 +2689,7 @@ pushScreenGrab (CompScreen *s,
+ Cursor cursor,
+ const char *name)
+ {
++
+ if (s->maxGrab == 0)
+ {
+ int status;
+diff --git a/src/window.c b/src/window.c
+index afb26b6..f69a072 100644
+--- a/src/window.c
++++ b/src/window.c
+@@ -2110,6 +2110,12 @@ addWindow (CompScreen *screen,
+
+ w->saveMask = 0;
+
++ /* Input Meshing */
++
++ w->nMesh = 1;
++ w->meshMin = 0;
++ w->meshMax = 0;
++
+ XSelectInput (d->display, id,
+ PropertyChangeMask |
+ EnterWindowMask |
+@@ -2138,6 +2144,8 @@ addWindow (CompScreen *screen,
+ ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
+ GrabModeSync, GrabModeSync, None, None);
+
++
++
+ w->inputHint = TRUE;
+ w->alpha = (w->attrib.depth == 32);
+ w->wmType = 0;
+@@ -2305,6 +2313,8 @@ addWindow (CompScreen *screen,
+
+ (*core.objectAdd) (&screen->base, &w->base);
+
++ updateMesh (w);
++
+ recalcWindowActions (w);
+ updateIconGeometry (w);
+
+@@ -2947,6 +2957,7 @@ windowResizeNotify (CompWindow *w,
+ int dwidth,
+ int dheight)
+ {
++ updateMesh (w);
+ }
+
+ void
+@@ -2955,6 +2966,7 @@ windowMoveNotify (CompWindow *w,
+ int dy,
+ Bool immediate)
+ {
++ updateMesh (w);
+ }
+
+ void
+@@ -3458,6 +3470,8 @@ reconfigureXWindow (CompWindow *w,
+ if (w->frame && (valueMask & (CWSibling | CWStackMode)))
+ XConfigureWindow (w->screen->display->display, w->frame,
+ valueMask & (CWSibling | CWStackMode), xwc);
++
++ //updateMesh (w);
+ }
+
+ static Bool
+--
+1.5.6
+
diff --git a/compiz/0004-CompMesh-based-Input-Redirection-support.patch b/compiz/0004-CompMesh-based-Input-Redirection-support.patch
new file mode 100644
index 0000000..3696ad9
--- /dev/null
+++ b/compiz/0004-CompMesh-based-Input-Redirection-support.patch
@@ -0,0 +1,746 @@
+From a70be5cadf6bc3619e661143a4767c59a897443c Mon Sep 17 00:00:00 2001
+From: Sam Spilsbury <Sam@XPS-SUSE.site>
+Date: Fri, 31 Oct 2008 14:53:43 +0900
+Subject: [PATCH] CompMesh based Input Redirection support
+
+---
+ include/compiz-scale.h | 31 ++++---
+ plugins/Makefile.am | 2 +-
+ plugins/annotate.c | 212 ++++++++++++++++++++++++++++++++++++++----------
+ plugins/move.c | 4 +
+ plugins/scale.c | 146 ++++++++++++++++++++++++++++++---
+ 5 files changed, 326 insertions(+), 69 deletions(-)
+
+diff --git a/include/compiz-scale.h b/include/compiz-scale.h
+index 118c27d..1611d4b 100644
+--- a/include/compiz-scale.h
++++ b/include/compiz-scale.h
+@@ -57,6 +57,14 @@ typedef struct _SlotArea {
+ XRectangle workArea;
+ } SlotArea;
+
++typedef struct _ScaleDevice {
++ CompDevice *dev;
++ int grabIndex;
++ Window hoveredWindow;
++ Window selectedWindow;
++ struct _ScaleDevice *next;
++} ScaleDevice;
++
+ #define SCALE_DISPLAY_OPTION_ABI 0
+ #define SCALE_DISPLAY_OPTION_INDEX 1
+ #define SCALE_DISPLAY_OPTION_INITIATE_EDGE 2
+@@ -73,17 +81,10 @@ typedef struct _SlotArea {
+ #define SCALE_DISPLAY_OPTION_INITIATE_OUTPUT_KEY 13
+ #define SCALE_DISPLAY_OPTION_SHOW_DESKTOP 14
+ #define SCALE_DISPLAY_OPTION_RELAYOUT 15
+-#define SCALE_DISPLAY_OPTION_KEY_BINDINGS_TOGGLE 16
+-#define SCALE_DISPLAY_OPTION_BUTTON_BINDINGS_TOGGLE 17
+-#define SCALE_DISPLAY_OPTION_NUM 18
+-
+-typedef struct _ScaleDevice {
+- CompDevice *dev;
+- int grabIndex;
+- Window hoveredWindow;
+- Window selectedWindow;
+- struct _ScaleDevice *next;
+-} ScaleDevice;
++#define SCALE_DISPLAY_OPTION_DOUBLE_CLICK_TIME 16
++#define SCALE_DISPLAY_OPTION_KEY_BINDINGS_TOGGLE 17
++#define SCALE_DISPLAY_OPTION_BUTTON_BINDINGS_TOGGLE 18
++#define SCALE_DISPLAY_OPTION_NUM 19
+
+ typedef struct _ScaleDisplay {
+ int screenPrivateIndex;
+@@ -140,6 +141,7 @@ typedef struct _ScaleScreen {
+ PaintOutputProc paintOutput;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
++ TransformMeshProc transformMesh;
+
+ ScaleLayoutSlotsAndAssignWindowsProc layoutSlotsAndAssignWindows;
+ ScaleSetScaledPaintAttributesProc setScaledPaintAttributes;
+@@ -158,19 +160,22 @@ typedef struct _ScaleScreen {
+ int state;
+ int moreAdjust;
+
++ int clickTime;
++ Bool doubleClick;
++
+ Cursor cursor;
+
+ ScaleSlot *slots;
+ int slotsSize;
+ int nSlots;
+
++ ScaleDevice *devices;
++
+ /* only used for sorting */
+ CompWindow **windows;
+ int windowsSize;
+ int nWindows;
+
+- ScaleDevice *devices;
+-
+ GLushort opacity;
+
+ ScaleType type;
+diff --git a/plugins/Makefile.am b/plugins/Makefile.am
+index 66e4ed7..139e143 100644
+--- a/plugins/Makefile.am
++++ b/plugins/Makefile.am
+@@ -51,7 +51,7 @@ libpng_la_SOURCES = png.c
+
+ libblur_la_DEPENDENCIES = $(top_builddir)/libdecoration/libdecoration.la
+ libblur_la_LDFLAGS = -module -avoid-version -no-undefined
+-libblur_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la -lGLU
++libblur_la_LIBADD = $(top_builddir)/libdecoration/libdecoration.la
+ libblur_la_SOURCES = blur.c
+
+ libregex_la_LDFLAGS = -module -avoid-version -no-undefined
+diff --git a/plugins/annotate.c b/plugins/annotate.c
+index 6271260..4827bcf 100644
+--- a/plugins/annotate.c
++++ b/plugins/annotate.c
+@@ -34,9 +34,6 @@ static CompMetadata annoMetadata;
+
+ static int displayPrivateIndex;
+
+-static int annoLastPointerX = 0;
+-static int annoLastPointerY = 0;
+-
+ #define ANNO_DISPLAY_OPTION_INITIATE_BUTTON 0
+ #define ANNO_DISPLAY_OPTION_DRAW_BUTTON 1
+ #define ANNO_DISPLAY_OPTION_ERASE_BUTTON 2
+@@ -48,24 +45,33 @@ static int annoLastPointerY = 0;
+ #define ANNO_DISPLAY_OPTION_STROKE_WIDTH 8
+ #define ANNO_DISPLAY_OPTION_NUM 9
+
++typedef struct _AnnoDevice {
++ CompDevice *dev;
++ int grabIndex;
++ int annoLastPointerX;
++ int annoLastPointerY;
++
++ Bool eraseMode;
++} AnnoDevice;
++
+ typedef struct _AnnoDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompOption opt[ANNO_DISPLAY_OPTION_NUM];
++
++ AnnoDevice *devices;
++ int ndevices;
+ } AnnoDisplay;
+
+ typedef struct _AnnoScreen {
+ PaintOutputProc paintOutput;
+- int grabIndex;
+
+ Pixmap pixmap;
+ CompTexture texture;
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+ Bool content;
+-
+- Bool eraseMode;
+ } AnnoScreen;
+
+ #define GET_ANNO_DISPLAY(d) \
+@@ -85,6 +91,16 @@ typedef struct _AnnoScreen {
+
+ #define NUM_TOOLS (sizeof (tools) / sizeof (tools[0]))
+
++static AnnoDevice*
++annoGetDeviceByDev (AnnoDisplay *d, CompDevice* dev)
++{
++ int i = 0;
++ for (i = 0; i < d->ndevices; i ++)
++ if (d->devices[i].dev == dev)
++ return &d->devices[i];
++ return NULL;
++}
++
+ static void
+ annoCairoClear (CompScreen *s,
+ cairo_t *cr)
+@@ -458,13 +474,47 @@ annoInitiate (CompDisplay *d,
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+- ANNO_SCREEN (s);
++ int id;
++ CompDevice *dev;
++ AnnoDevice *adev;
++ ANNO_DISPLAY(d);
++
++ id = getIntOptionNamed(option, nOption, "device", -1);
++ dev = compFindDeviceById(d, id);
++ adev = annoGetDeviceByDev(ad, dev);
++
++ if (dev && otherDeviceGrabExist(dev, 0))
++ return FALSE;
++ else if (!dev && otherScreenGrabExist (s, 0))
++ return FALSE;
+
+- if (otherScreenGrabExist (s, 0))
++ ad->ndevices++;
++ ad->devices = realloc(ad->devices, ad->ndevices * sizeof(AnnoDevice));
++ if (!ad->devices)
+ return FALSE;
+
+- if (!as->grabIndex)
+- as->grabIndex = pushScreenGrab (s, None, "annotate");
++ adev = &ad->devices[ad->ndevices - 1];
++ adev->dev = dev;
++ adev->grabIndex = 0;
++ adev->eraseMode = FALSE;
++
++ if (dev)
++ {
++ adev->annoLastPointerX = dev->pointerX;
++ adev->annoLastPointerY = dev->pointerY;
++ } else
++ {
++ adev->annoLastPointerX = pointerX;
++ adev->annoLastPointerY = pointerY;
++ }
++
++ if (!adev->grabIndex) /* XXX */
++ {
++ if (dev)
++ adev->grabIndex = pushDeviceGrab (s, dev, None, "annotate");
++ else
++ adev->grabIndex = pushScreenGrab (s, None, "annotate");
++ }
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+@@ -472,10 +522,6 @@ annoInitiate (CompDisplay *d,
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+
+- annoLastPointerX = pointerX;
+- annoLastPointerY = pointerY;
+-
+- as->eraseMode = FALSE;
+ }
+
+ return TRUE;
+@@ -490,25 +536,47 @@ annoTerminate (CompDisplay *d,
+ {
+ CompScreen *s;
+ Window xid;
++ int id;
++ CompDevice *dev;
++ AnnoDevice *adev;
++ ANNO_DISPLAY(d);
+
+ xid = getIntOptionNamed (option, nOption, "root", 0);
++ id = getIntOptionNamed(option, nOption, "device", -1);
++ dev = compFindDeviceById(d, id);
++ adev = annoGetDeviceByDev(ad, dev);
+
+ for (s = d->screens; s; s = s->next)
+ {
+- ANNO_SCREEN (s);
+-
+ if (xid && s->root != xid)
+ continue;
+
+- if (as->grabIndex)
++ if (adev && adev->grabIndex)
+ {
+- removeScreenGrab (s, as->grabIndex, NULL);
+- as->grabIndex = 0;
++ int i;
++
++ if (dev)
++ removeDeviceGrab(s, dev, adev->grabIndex, NULL);
++ else
++ removeScreenGrab (s, adev->grabIndex, NULL);
++ adev->grabIndex = 0;
++
++
++ ad->ndevices--;
++ for (i = 0; i < ad->ndevices; i++)
++ {
++ if (adev == &ad->devices[i])
++ {
++ memmove(adev, adev + 1, ad->ndevices - i);
++ break;
++ }
++ }
++ ad->devices = realloc(ad->devices, ad->ndevices * sizeof(AnnoDevice));
++ if (!ad->devices)
++ action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+ }
+ }
+
+- action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);
+-
+ return FALSE;
+ }
+
+@@ -527,24 +595,55 @@ annoEraseInitiate (CompDisplay *d,
+ s = findScreenAtDisplay (d, xid);
+ if (s)
+ {
+- ANNO_SCREEN (s);
++ int id;
++ CompDevice *dev;
++ AnnoDevice *adev;
++
++ ANNO_DISPLAY(d);
++
++ id = getIntOptionNamed(option, nOption, "device", -1);
++ dev = compFindDeviceById(d, id);
++ adev = annoGetDeviceByDev(ad, dev);
+
+- if (otherScreenGrabExist (s, 0))
++ if (dev && otherDeviceGrabExist(dev, 0))
+ return FALSE;
++ else if (!dev && otherScreenGrabExist (s, 0))
++ return FALSE;
++
++ ad->ndevices++;
++ ad->devices = realloc(ad->devices, ad->ndevices * sizeof(AnnoDevice));
++ if (!ad->devices)
++ return FALSE;
++
++ adev = &ad->devices[ad->ndevices - 1];
++ adev->dev = dev;
++ adev->grabIndex = 0;
++ adev->eraseMode = TRUE;
++
++ if (dev)
++ {
++ adev->annoLastPointerX = dev->pointerX;
++ adev->annoLastPointerY = dev->pointerY;
++ } else
++ {
++ adev->annoLastPointerX = pointerX;
++ adev->annoLastPointerY = pointerY;
++ }
++
++ if (!adev->grabIndex) /* XXX */
++ {
++ if (dev)
++ adev->grabIndex = pushDeviceGrab (s, dev, None, "annotate");
++ else
++ adev->grabIndex = pushScreenGrab (s, None, "annotate");
++ }
+
+- if (!as->grabIndex)
+- as->grabIndex = pushScreenGrab (s, None, "annotate");
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+-
+- annoLastPointerX = pointerX;
+- annoLastPointerY = pointerY;
+-
+- as->eraseMode = TRUE;
+ }
+
+ return FALSE;
+@@ -652,35 +751,36 @@ annoPaintOutput (CompScreen *s,
+
+ static void
+ annoHandleMotionEvent (CompScreen *s,
++ CompDevice *dev,
+ int xRoot,
+ int yRoot)
+ {
+- ANNO_SCREEN (s);
++ AnnoDevice *adev;
++ ANNO_DISPLAY(s->display);
+
+- if (as->grabIndex)
++ adev = annoGetDeviceByDev(ad, dev);
++ if (adev && adev->grabIndex)
+ {
+- if (as->eraseMode)
++ if (adev->eraseMode)
+ {
+ static unsigned short color[] = { 0, 0, 0, 0 };
+
+ annoDrawLine (s,
+- annoLastPointerX, annoLastPointerY,
++ adev->annoLastPointerX, adev->annoLastPointerY,
+ xRoot, yRoot,
+ 20.0, color);
+ }
+ else
+ {
+- ANNO_DISPLAY(s->display);
+-
+ annoDrawLine (s,
+- annoLastPointerX, annoLastPointerY,
++ adev->annoLastPointerX, adev->annoLastPointerY,
+ xRoot, yRoot,
+ ad->opt[ANNO_DISPLAY_OPTION_LINE_WIDTH].value.f,
+ ad->opt[ANNO_DISPLAY_OPTION_FILL_COLOR].value.c);
+ }
+
+- annoLastPointerX = xRoot;
+- annoLastPointerY = yRoot;
++ adev->annoLastPointerX = xRoot;
++ adev->annoLastPointerY = yRoot;
+ }
+ }
+
+@@ -696,14 +796,39 @@ annoHandleEvent (CompDisplay *d,
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+- annoHandleMotionEvent (s, pointerX, pointerY);
++ annoHandleMotionEvent (s, NULL, pointerX, pointerY);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ s = findScreenAtDisplay (d, event->xcrossing.root);
+ if (s)
+- annoHandleMotionEvent (s, pointerX, pointerY);
++ annoHandleMotionEvent (s, NULL, pointerX, pointerY);
+ default:
++ if (event->type == d->xi_motion)
++ {
++ CompDevice *dev;
++ XDeviceMotionEvent *mev = (XDeviceMotionEvent*)event;
++ s = findScreenAtDisplay (d, mev->root);
++ if (s)
++ {
++ dev = compFindDeviceById (d, mev->deviceid);
++ annoHandleMotionEvent (s, dev, dev->pointerX, dev->pointerY);
++ }
++ break;
++ }
++
++ if (event->type == d->xi_enter || event->type == d->xi_leave)
++ {
++ CompDevice *dev;
++ XDeviceCrossingEvent *crev = (XDeviceCrossingEvent*)event;
++ s = findScreenAtDisplay (d, crev->root);
++ if (s)
++ {
++ dev = compFindDeviceById (d, crev->deviceid);
++ annoHandleMotionEvent (s, dev, dev->pointerX, dev->pointerY);
++ }
++ break;
++ }
+ break;
+ }
+
+@@ -783,6 +908,9 @@ annoInitDisplay (CompPlugin *p,
+ return FALSE;
+ }
+
++ ad->devices = NULL;
++ ad->ndevices = 0;
++
+ WRAP (ad, d, handleEvent, annoHandleEvent);
+
+ d->base.privates[displayPrivateIndex].ptr = ad;
+@@ -802,6 +930,7 @@ annoFiniDisplay (CompPlugin *p,
+
+ compFiniDisplayOptions (d, ad->opt, ANNO_DISPLAY_OPTION_NUM);
+
++ free (ad->devices);
+ free (ad);
+ }
+
+@@ -817,7 +946,6 @@ annoInitScreen (CompPlugin *p,
+ if (!as)
+ return FALSE;
+
+- as->grabIndex = 0;
+ as->surface = NULL;
+ as->pixmap = None;
+ as->cairo = NULL;
+diff --git a/plugins/move.c b/plugins/move.c
+index 4c77239..f74d477 100644
+--- a/plugins/move.c
++++ b/plugins/move.c
+@@ -697,6 +697,10 @@ moveHandleMotionEvent (CompScreen *s,
+
+ if (dx || dy)
+ {
++ moveWindow (w,
++ wX + dx - w->attrib.x,
++ wY + dy - w->attrib.y,
++ TRUE, FALSE);
+
+ if (md->opt[MOVE_DISPLAY_OPTION_LAZY_POSITIONING].value.b)
+ {
+diff --git a/plugins/scale.c b/plugins/scale.c
+index 40989b2..9e77a09 100644
+--- a/plugins/scale.c
++++ b/plugins/scale.c
+@@ -155,6 +155,41 @@ isScaleWin (CompWindow *w)
+ /* Device Management */
+
+ static Bool
++scaleUpdateHoveredWindows (CompScreen *s)
++{
++ Window root_return;
++ Window child_return;
++ int rootX, rootY;
++ int winX, winY;
++ unsigned int maskReturn;
++ Bool status;
++ ScaleDevice *dev;
++
++ SCALE_SCREEN (s);
++
++ for (dev = ss->devices; dev; dev = dev->next)
++ {
++
++ status = XQueryDevicePointer (s->display->display, dev->dev->dev, s->root,
++ &root_return, &child_return,
++ &rootX, &rootY, &winX, &winY, &maskReturn);
++
++ if (!status || rootX > s->width || rootY > s->height ||
++ s->root != root_return)
++ return FALSE;
++
++
++ scaleSelectWindowAt (s,
++ dev->dev,
++ rootX,
++ rootY,
++ FALSE);
++
++ }
++ return TRUE;
++}
++
++static Bool
+ scaleGrabDevice (CompScreen *s,
+ ScaleDevice *sDev)
+ {
+@@ -585,6 +620,8 @@ scalePaintWindow (CompWindow *w,
+ sw->ty / sw->scale - w->attrib.y,
+ 0.0f);
+
++ updateMesh (w);
++
+ glPushMatrix ();
+ glLoadMatrixf (wTransform.m);
+
+@@ -1047,6 +1084,8 @@ scalePaintOutput (CompScreen *s,
+ if (ss->state != SCALE_STATE_NONE)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
++ scaleUpdateHoveredWindows (s);
++
+ UNWRAP (ss, s, paintOutput);
+ status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+ WRAP (ss, s, paintOutput, scalePaintOutput);
+@@ -1189,6 +1228,7 @@ sendDndStatusMessage (CompScreen *s,
+
+ XSendEvent (s->display->display, source, FALSE, 0, &xev);
+ }
++
+ static Bool
+ scaleTerminate (CompDisplay *d,
+ CompAction *action,
+@@ -1227,6 +1267,7 @@ scaleTerminate (CompDisplay *d,
+
+ SCALE_SCREEN (s);
+
++ if (!d->opt[COMP_DISPLAY_OPTION_REDIRECT_INPUT].value.b)
+ for (sDev = ss->devices; sDev; sDev = sDev->next)
+ {
+ removeDeviceGrab (s, sDev->dev, sDev->grabIndex, NULL);
+@@ -1372,6 +1413,7 @@ scaleInitiateCommon (CompScreen *s,
+ }
+ scaleCheckList (s);
+
++ if (!s->display->opt[COMP_DISPLAY_OPTION_REDIRECT_INPUT].value.b)
+ for (sDev = ss->devices; sDev; sDev = sDev->next)
+ {
+ ss->grab = scaleGrabDevice (s, sDev);
+@@ -1896,7 +1938,6 @@ scaleHoverTimeout (void *closure)
+ return FALSE;
+ }
+
+-
+ static void
+ scaleHandleEvent (CompDisplay *d,
+ XEvent *event)
+@@ -1913,7 +1954,7 @@ scaleHandleEvent (CompDisplay *d,
+ {
+ SCALE_SCREEN (s);
+
+- if (ss->grabIndex)
++ if (ss->grabIndex && !d->opt[COMP_DISPLAY_OPTION_REDIRECT_INPUT].value.b)
+ {
+ if (event->xkey.keycode == sd->leftKeyCode)
+ {
+@@ -1948,7 +1989,23 @@ scaleHandleEvent (CompDisplay *d,
+
+ SCALE_SCREEN (s);
+
+- if (ss->grabIndex && ss->state != SCALE_STATE_IN)
++ if (ss->clickTime == 0)
++ {
++ ss->clickTime = event->xbutton.time;
++ }
++ else if ((event->xbutton.time - ss->clickTime) <=
++ sd->opt[SCALE_DISPLAY_OPTION_DOUBLE_CLICK_TIME].value.i)
++ {
++
++ ss->doubleClick = TRUE;
++ }
++ else
++ {
++ ss->clickTime = event->xbutton.time;
++ ss->doubleClick = FALSE;
++ }
++
++ if (ss->grabIndex && ss->state != SCALE_STATE_IN && (ss->doubleClick))
+ {
+ CompOption o;
+
+@@ -2116,16 +2173,28 @@ scaleHandleEvent (CompDisplay *d,
+ {
+ SCALE_SCREEN (s);
+
+- if (ss->grab)
++ if (ss->grab && !d->opt[COMP_DISPLAY_OPTION_REDIRECT_INPUT].value.b)
+ {
+- if (kev->keycode == sd->leftKeyCode)
+- scaleMoveFocusWindow (s, dev, -1, 0);
+- else if (kev->keycode == sd->rightKeyCode)
+- scaleMoveFocusWindow (s, dev, 1, 0);
+- else if (kev->keycode == sd->upKeyCode)
+- scaleMoveFocusWindow (s, dev, 0, -1);
+- else if (kev->keycode == sd->downKeyCode)
+- scaleMoveFocusWindow (s, dev, 0, 1);
++ if (event->xkey.keycode == sd->leftKeyCode)
++ {
++ scaleMoveFocusWindow (s, NULL, -1, 0);
++ consumeEvent = TRUE;
++ }
++ else if (event->xkey.keycode == sd->rightKeyCode)
++ {
++ scaleMoveFocusWindow (s, NULL, 1, 0);
++ consumeEvent = TRUE;
++ }
++ else if (event->xkey.keycode == sd->upKeyCode)
++ {
++ scaleMoveFocusWindow (s, NULL, 0, -1);
++ consumeEvent = TRUE;
++ }
++ else if (event->xkey.keycode == sd->downKeyCode)
++ {
++ scaleMoveFocusWindow (s, NULL, 0, 1);
++ consumeEvent = TRUE;
++ }
+ }
+ }
+ }
+@@ -2142,7 +2211,22 @@ scaleHandleEvent (CompDisplay *d,
+
+ SCALE_SCREEN (s);
+
+- if (ss->grab && ss->state != SCALE_STATE_IN)
++ if (ss->clickTime == 0)
++ {
++ ss->clickTime = event->xbutton.time;
++ }
++ else if (bev->time - ss->clickTime <=
++ sd->opt[SCALE_DISPLAY_OPTION_DOUBLE_CLICK_TIME].value.i)
++ {
++ ss->doubleClick = TRUE;
++ }
++ else
++ {
++ ss->clickTime = bev->time;
++ ss->doubleClick = FALSE;
++ }
++
++ if (ss->grab && ss->state != SCALE_STATE_IN && ss->doubleClick)
+ {
+ int nOption = 0;
+ CompOption o[2];
+@@ -2285,6 +2369,36 @@ scaleDamageWindowRect (CompWindow *w,
+ return status;
+ }
+
++static void
++scaleTransformMesh (CompWindow *w,
++ CompTransform *transform,
++ CompMesh *mesh,
++ int meshIter,
++ Bool needsProjection)
++{
++ SCALE_SCREEN (w->screen);
++ CompTransform scaleTransform = *transform;
++
++ if (TRUE)
++ {
++ SCALE_WINDOW (w);
++
++ if (sw->slot)
++ {
++ matrixTranslate (&scaleTransform, w->attrib.x, w->attrib.y, 0.0f);
++ matrixScale (&scaleTransform, sw->scale, sw->scale, 1.0f);
++ matrixTranslate (&scaleTransform,
++ sw->tx / sw->scale - w->attrib.x,
++ sw->ty / sw->scale - w->attrib.y,
++ 0.0f);
++ }
++ }
++
++ UNWRAP (ss, w->screen, transformMesh);
++ (*w->screen->transformMesh) (w, &scaleTransform, mesh, meshIter, needsProjection);
++ WRAP (ss, w->screen, transformMesh, scaleTransformMesh);
++}
++
+ static CompOption *
+ scaleGetDisplayOptions (CompPlugin *plugin,
+ CompDisplay *display,
+@@ -2341,6 +2455,7 @@ static const CompMetadataOptionInfo scaleDisplayOptionInfo[] = {
+ { "initiate_output_key", "key", 0, scaleInitiateOutput, scaleTerminate },
+ { "show_desktop", "bool", 0, 0, 0 },
+ { "relayout_slots", "action", 0, scaleRelayoutSlots, 0 },
++ { "double_click_time", "int", 0, 0, 0 },
+ { "key_bindings_toggle", "bool", 0, 0, 0 },
+ { "button_bindings_toggle", "bool", 0, 0, 0 }
+ };
+@@ -2472,6 +2587,9 @@ scaleInitScreen (CompPlugin *p,
+ ss->opacity =
+ (OPAQUE * ss->opt[SCALE_SCREEN_OPTION_OPACITY].value.i) / 100;
+
++ ss->clickTime = 0;
++ ss->doubleClick = FALSE;
++
+ matchInit (&ss->match);
+
+ ss->layoutSlotsAndAssignWindows = layoutSlotsAndAssignWindows;
+@@ -2484,6 +2602,7 @@ scaleInitScreen (CompPlugin *p,
+ WRAP (ss, s, paintOutput, scalePaintOutput);
+ WRAP (ss, s, paintWindow, scalePaintWindow);
+ WRAP (ss, s, damageWindowRect, scaleDamageWindowRect);
++ WRAP (ss, s, transformMesh, scaleTransformMesh);
+
+ ss->cursor = XCreateFontCursor (s->display->display, XC_left_ptr);
+
+@@ -2503,6 +2622,7 @@ scaleFiniScreen (CompPlugin *p,
+ UNWRAP (ss, s, paintOutput);
+ UNWRAP (ss, s, paintWindow);
+ UNWRAP (ss, s, damageWindowRect);
++ UNWRAP (ss, s, transformMesh);
+
+ matchFini (&ss->match);
+
+--
+1.5.6
+