You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3055 lines
64 KiB

/*
* Copyright © 2005 Novell, Inc.
*
* 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
* Novell, Inc. not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
* Novell, Inc. makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL NOVELL, INC. 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: David Reveman <davidr@novell.com>
*/
/*
* Spring model implemented by Kristian Hogsberg.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <compiz-core.h>
#define WIN_X(w) ((w)->attrib.x - (w)->output.left)
#define WIN_Y(w) ((w)->attrib.y - (w)->output.top)
#define WIN_W(w) ((w)->width + (w)->output.left + (w)->output.right)
#define WIN_H(w) ((w)->height + (w)->output.top + (w)->output.bottom)
#define GRID_WIDTH 4
#define GRID_HEIGHT 4
#define MODEL_MAX_SPRINGS (GRID_WIDTH * GRID_HEIGHT * 2)
#define MASS 15.0f
typedef struct _xy_pair {
float x, y;
} Point, Vector;
#define NorthEdgeMask (1L << 0)
#define SouthEdgeMask (1L << 1)
#define WestEdgeMask (1L << 2)
#define EastEdgeMask (1L << 3)
#define EDGE_DISTANCE 25.0f
#define EDGE_VELOCITY 13.0f
typedef struct _Edge {
float next, prev;
float start;
float end;
float attract;
float velocity;
Bool snapped;
} Edge;
typedef struct _Object {
Vector force;
Point position;
Vector velocity;
float theta;
Bool immobile;
unsigned int edgeMask;
Edge vertEdge;
Edge horzEdge;
} Object;
typedef struct _Spring {
Object *a;
Object *b;
Vector offset;
} Spring;
#define NORTH 0
#define SOUTH 1
#define WEST 2
#define EAST 3
typedef struct _Model {
Object *objects;
int numObjects;
Spring springs[MODEL_MAX_SPRINGS];
int numSprings;
Object *anchorObject;
float steps;
Point topLeft;
Point bottomRight;
unsigned int edgeMask;
unsigned int snapCnt[4];
} Model;
#define WOBBLY_EFFECT_NONE 0
#define WOBBLY_EFFECT_SHIVER 1
#define WOBBLY_EFFECT_LAST WOBBLY_EFFECT_SHIVER
static CompMetadata wobblyMetadata;
static int displayPrivateIndex;
#define WOBBLY_DISPLAY_OPTION_SNAP_KEY 0
#define WOBBLY_DISPLAY_OPTION_SNAP_INVERTED 1
#define WOBBLY_DISPLAY_OPTION_SHIVER 2
#define WOBBLY_DISPLAY_OPTION_NUM 3
typedef struct _WobblyDisplay {
int screenPrivateIndex;
HandleEventProc handleEvent;
CompOption opt[WOBBLY_DISPLAY_OPTION_NUM];
Bool snapping;
Bool yConstrained;
} WobblyDisplay;
#define WOBBLY_SCREEN_OPTION_FRICTION 0
#define WOBBLY_SCREEN_OPTION_SPRING_K 1
#define WOBBLY_SCREEN_OPTION_GRID_RESOLUTION 2
#define WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE 3
#define WOBBLY_SCREEN_OPTION_MAP_EFFECT 4
#define WOBBLY_SCREEN_OPTION_FOCUS_EFFECT 5
#define WOBBLY_SCREEN_OPTION_MAP_WINDOW_MATCH 6
#define WOBBLY_SCREEN_OPTION_FOCUS_WINDOW_MATCH 7
#define WOBBLY_SCREEN_OPTION_GRAB_WINDOW_MATCH 8
#define WOBBLY_SCREEN_OPTION_MOVE_WINDOW_MATCH 9
#define WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT 10
#define WOBBLY_SCREEN_OPTION_NUM 11
typedef struct _WobblyScreen {
int windowPrivateIndex;
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
PaintOutputProc paintOutput;
PaintWindowProc paintWindow;
DamageWindowRectProc damageWindowRect;
AddWindowGeometryProc addWindowGeometry;
WindowResizeNotifyProc windowResizeNotify;
WindowMoveNotifyProc windowMoveNotify;
WindowGrabNotifyProc windowGrabNotify;
WindowUngrabNotifyProc windowUngrabNotify;
CompOption opt[WOBBLY_SCREEN_OPTION_NUM];
Bool wobblyWindows;
unsigned int grabMask;
CompWindow *grabWindow;
Bool moveWindow;
const XRectangle *grabWindowWorkArea;
} WobblyScreen;
#define WobblyInitial (1L << 0)
#define WobblyForce (1L << 1)
#define WobblyVelocity (1L << 2)
typedef struct _WobblyWindow {
Model *model;
int wobbly;
Bool grabbed;
Bool velocity;
unsigned int state;
} WobblyWindow;
#define GET_WOBBLY_DISPLAY(d) \
((WobblyDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
#define WOBBLY_DISPLAY(d) \
WobblyDisplay *wd = GET_WOBBLY_DISPLAY (d)
#define GET_WOBBLY_SCREEN(s, wd) \
((WobblyScreen *) (s)->base.privates[(wd)->screenPrivateIndex].ptr)
#define WOBBLY_SCREEN(s) \
WobblyScreen *ws = GET_WOBBLY_SCREEN (s, GET_WOBBLY_DISPLAY (s->display))
#define GET_WOBBLY_WINDOW(w, ws) \
((WobblyWindow *) (w)->base.privates[(ws)->windowPrivateIndex].ptr)
#define WOBBLY_WINDOW(w) \
WobblyWindow *ww = GET_WOBBLY_WINDOW (w, \
GET_WOBBLY_SCREEN (w->screen, \
GET_WOBBLY_DISPLAY (w->screen->display)))
#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
static CompOption *
wobblyGetScreenOptions (CompPlugin *plugin,
CompScreen *screen,
int *count)
{
WOBBLY_SCREEN (screen);
*count = NUM_OPTIONS (ws);
return ws->opt;
}
static Bool
wobblySetScreenOption (CompPlugin *plugin,
CompScreen *screen,
const char *name,
CompOptionValue *value)
{
CompOption *o;
WOBBLY_SCREEN (screen);
o = compFindOption (ws->opt, NUM_OPTIONS (ws), name, NULL);
if (!o)
return FALSE;
return compSetScreenOption (screen, o, value);
}
static const CompMetadataOptionInfo wobblyScreenOptionInfo[] = {
{ "friction", "float", "<min>0.1</min>", 0, 0 },
{ "spring_k", "float", "<min>0.1</min>", 0, 0 },
{ "grid_resolution", "int", "<min>1</min><max>64</max>", 0, 0 },
{ "min_grid_size", "int", "<min>4</min><max>128</max>", 0, 0 },
{ "map_effect", "int", RESTOSTRING (0, WOBBLY_EFFECT_LAST), 0, 0 },
{ "focus_effect", "int", RESTOSTRING (0, WOBBLY_EFFECT_LAST), 0, 0 },
{ "map_window_match", "match", 0, 0, 0 },
{ "focus_window_match", "match", 0, 0, 0 },
{ "grab_window_match", "match", 0, 0, 0 },
{ "move_window_match", "match", 0, 0, 0 },
{ "maximize_effect", "bool", 0, 0, 0 }
};
#define SNAP_WINDOW_TYPE (CompWindowTypeNormalMask | \
CompWindowTypeToolbarMask | \
CompWindowTypeMenuMask | \
CompWindowTypeUtilMask)
static void
findNextWestEdge (CompWindow *w,
Object *object)
{
int v, v1, v2;
int s, start;
int e, end;
int x;
int output;
const XRectangle *workArea;
int workAreaEdge;
start = -65535.0f;
end = 65535.0f;
v1 = -65535.0f;
v2 = 65535.0f;
x = object->position.x + w->output.left - w->input.left;
output = outputDeviceForPoint (w->screen, x, object->position.y);
workArea = &w->screen->outputDev[output].workArea;
workAreaEdge = workArea->x;
if (x >= workAreaEdge)
{
CompWindow *p;
v1 = workAreaEdge;
for (p = w->screen->windows; p; p = p->next)
{
if (w == p)
continue;
if (p->mapNum && p->struts)
{
s = p->struts->left.y - w->output.top;
e = p->struts->left.y + p->struts->left.height +
w->output.bottom;
}
else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
{
s = p->attrib.y - p->input.top - w->output.top;
e = p->attrib.y + p->height + p->input.bottom +
w->output.bottom;
}
else
{
continue;
}
if (s > object->position.y)
{
if (s < end)
end = s;
}
else if (e < object->position.y)
{
if (e > start)
start = e;
}
else
{
if (s > start)
start = s;
if (e < end)
end = e;
if (p->mapNum && p->struts)
v = p->struts->left.x + p->struts->left.width;
else
v = p->attrib.x + p->width + p->input.right;
if (v <= x)
{
if (v > v1)
v1 = v;
}
else
{
if (v < v2)
v2 = v;
}
}
}
}
else
{
v2 = workAreaEdge;
}
v1 = v1 - w->output.left + w->input.left;
v2 = v2 - w->output.left + w->input.left;
if (v1 != (int) object->vertEdge.next)
object->vertEdge.snapped = FALSE;
object->vertEdge.start = start;
object->vertEdge.end = end;
object->vertEdge.next = v1;
object->vertEdge.prev = v2;
object->vertEdge.attract = v1 + EDGE_DISTANCE;
object->vertEdge.velocity = EDGE_VELOCITY;
}
static void
findNextEastEdge (CompWindow *w,
Object *object)
{
int v, v1, v2;
int s, start;
int e, end;
int x;
int output;
const XRectangle *workArea;
int workAreaEdge;
start = -65535.0f;
end = 65535.0f;
v1 = 65535.0f;
v2 = -65535.0f;
x = object->position.x - w->output.right + w->input.right;
output = outputDeviceForPoint (w->screen, x, object->position.y);
workArea = &w->screen->outputDev[output].workArea;
workAreaEdge = workArea->x + workArea->width;
if (x <= workAreaEdge)
{
CompWindow *p;
v1 = workAreaEdge;
for (p = w->screen->windows; p; p = p->next)
{
if (w == p)
continue;
if (p->mapNum && p->struts)
{
s = p->struts->right.y - w->output.top;
e = p->struts->right.y + p->struts->right.height +
w->output.bottom;
}
else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
{
s = p->attrib.y - p->input.top - w->output.top;
e = p->attrib.y + p->height + p->input.bottom +
w->output.bottom;
}
else
{
continue;
}
if (s > object->position.y)
{
if (s < end)
end = s;
}
else if (e < object->position.y)
{
if (e > start)
start = e;
}
else
{
if (s > start)
start = s;
if (e < end)
end = e;
if (p->mapNum && p->struts)
v = p->struts->right.x;
else
v = p->attrib.x - p->input.left;
if (v >= x)
{
if (v < v1)
v1 = v;
}
else
{
if (v > v2)
v2 = v;
}
}
}
}
else
{
v2 = workAreaEdge;
}
v1 = v1 + w->output.right - w->input.right;
v2 = v2 + w->output.right - w->input.right;
if (v1 != (int) object->vertEdge.next)
object->vertEdge.snapped = FALSE;
object->vertEdge.start = start;
object->vertEdge.end = end;
object->vertEdge.next = v1;
object->vertEdge.prev = v2;
object->vertEdge.attract = v1 - EDGE_DISTANCE;
object->vertEdge.velocity = EDGE_VELOCITY;
}
static void
findNextNorthEdge (CompWindow *w,
Object *object)
{
int v, v1, v2;
int s, start;
int e, end;
int y;
int output;
const XRectangle *workArea;
int workAreaEdge;
start = -65535.0f;
end = 65535.0f;
v1 = -65535.0f;
v2 = 65535.0f;
y = object->position.y + w->output.top - w->input.top;
output = outputDeviceForPoint (w->screen, object->position.x, y);
workArea = &w->screen->outputDev[output].workArea;
workAreaEdge = workArea->y;
if (y >= workAreaEdge)
{
CompWindow *p;
v1 = workAreaEdge;
for (p = w->screen->windows; p; p = p->next)
{
if (w == p)
continue;
if (p->mapNum && p->struts)
{
s = p->struts->top.x - w->output.left;
e = p->struts->top.x + p->struts->top.width + w->output.right;
}
else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
{
s = p->attrib.x - p->input.left - w->output.left;
e = p->attrib.x + p->width + p->input.right + w->output.right;
}
else
{
continue;
}
if (s > object->position.x)
{
if (s < end)
end = s;
}
else if (e < object->position.x)
{
if (e > start)
start = e;
}
else
{
if (s > start)
start = s;
if (e < end)
end = e;
if (p->mapNum && p->struts)
v = p->struts->top.y + p->struts->top.height;
else
v = p->attrib.y + p->height + p->input.bottom;
if (v <= y)
{
if (v > v1)
v1 = v;
}
else
{
if (v < v2)
v2 = v;
}
}
}
}
else
{
v2 = workAreaEdge;
}
v1 = v1 - w->output.top + w->input.top;
v2 = v2 - w->output.top + w->input.top;
if (v1 != (int) object->horzEdge.next)
object->horzEdge.snapped = FALSE;
object->horzEdge.start = start;
object->horzEdge.end = end;
object->horzEdge.next = v1;
object->horzEdge.prev = v2;
object->horzEdge.attract = v1 + EDGE_DISTANCE;
object->horzEdge.velocity = EDGE_VELOCITY;
}
static void
findNextSouthEdge (CompWindow *w,
Object *object)
{
int v, v1, v2;
int s, start;
int e, end;
int y;
int output;
const XRectangle *workArea;
int workAreaEdge;
start = -65535.0f;
end = 65535.0f;
v1 = 65535.0f;
v2 = -65535.0f;
y = object->position.y - w->output.bottom + w->input.bottom;
output = outputDeviceForPoint (w->screen, object->position.x, y);
workArea = &w->screen->outputDev[output].workArea;
workAreaEdge = workArea->y + workArea->height;
if (y <= workAreaEdge)
{
CompWindow *p;
v1 = workAreaEdge;
for (p = w->screen->windows; p; p = p->next)
{
if (w == p)
continue;
if (p->mapNum && p->struts)
{
s = p->struts->bottom.x - w->output.left;
e = p->struts->bottom.x + p->struts->bottom.width +
w->output.right;
}
else if (!p->invisible && (p->type & SNAP_WINDOW_TYPE))
{
s = p->attrib.x - p->input.left - w->output.left;
e = p->attrib.x + p->width + p->input.right + w->output.right;
}
else
{
continue;
}
if (s > object->position.x)
{
if (s < end)
end = s;
}
else if (e < object->position.x)
{
if (e > start)
start = e;
}
else
{
if (s > start)
start = s;
if (e < end)
end = e;
if (p->mapNum && p->struts)
v = p->struts->bottom.y;
else
v = p->attrib.y - p->input.top;
if (v >= y)
{
if (v < v1)
v1 = v;
}
else
{
if (v > v2)
v2 = v;
}
}
}
}
else
{
v2 = workAreaEdge;
}
v1 = v1 + w->output.bottom - w->input.bottom;
v2 = v2 + w->output.bottom - w->input.bottom;
if (v1 != (int) object->horzEdge.next)
object->horzEdge.snapped = FALSE;
object->horzEdge.start = start;
object->horzEdge.end = end;
object->horzEdge.next = v1;
object->horzEdge.prev = v2;
object->horzEdge.attract = v1 - EDGE_DISTANCE;
object->horzEdge.velocity = EDGE_VELOCITY;
}
static void
objectInit (Object *object,
float positionX,
float positionY,
float velocityX,
float velocityY)
{
object->force.x = 0;
object->force.y = 0;
object->position.x = positionX;
object->position.y = positionY;
object->velocity.x = velocityX;
object->velocity.y = velocityY;
object->theta = 0;
object->immobile = FALSE;
object->edgeMask = 0;
object->vertEdge.snapped = FALSE;
object->horzEdge.snapped = FALSE;
object->vertEdge.next = 0.0f;
object->horzEdge.next = 0.0f;
}
static void
springInit (Spring *spring,
Object *a,
Object *b,
float offsetX,
float offsetY)
{
spring->a = a;
spring->b = b;
spring->offset.x = offsetX;
spring->offset.y = offsetY;
}
static void
modelCalcBounds (Model *model)
{
int i;
model->topLeft.x = MAXSHORT;
model->topLeft.y = MAXSHORT;
model->bottomRight.x = MINSHORT;
model->bottomRight.y = MINSHORT;
for (i = 0; i < model->numObjects; i++)
{
if (model->objects[i].position.x < model->topLeft.x)
model->topLeft.x = model->objects[i].position.x;
else if (model->objects[i].position.x > model->bottomRight.x)
model->bottomRight.x = model->objects[i].position.x;
if (model->objects[i].position.y < model->topLeft.y)
model->topLeft.y = model->objects[i].position.y;
else if (model->objects[i].position.y > model->bottomRight.y)
model->bottomRight.y = model->objects[i].position.y;
}
}
static void
modelAddSpring (Model *model,
Object *a,
Object *b,
float offsetX,
float offsetY)
{
Spring *spring;
spring = &model->springs[model->numSprings];
model->numSprings++;
springInit (spring, a, b, offsetX, offsetY);
}
static void
modelSetMiddleAnchor (Model *model,
int x,
int y,
int width,
int height)
{
float gx, gy;
gx = ((GRID_WIDTH - 1) / 2 * width) / (float) (GRID_WIDTH - 1);
gy = ((GRID_HEIGHT - 1) / 2 * height) / (float) (GRID_HEIGHT - 1);
if (model->anchorObject)
model->anchorObject->immobile = FALSE;
model->anchorObject = &model->objects[GRID_WIDTH *
((GRID_HEIGHT - 1) / 2) +
(GRID_WIDTH - 1) / 2];
model->anchorObject->position.x = x + gx;
model->anchorObject->position.y = y + gy;
model->anchorObject->immobile = TRUE;
}
static void
modelSetTopAnchor (Model *model,
int x,
int y,
int width)
{
float gx;
gx = ((GRID_WIDTH - 1) / 2 * width) / (float) (GRID_WIDTH - 1);
if (model->anchorObject)
model->anchorObject->immobile = FALSE;
model->anchorObject = &model->objects[(GRID_WIDTH - 1) / 2];
model->anchorObject->position.x = x + gx;
model->anchorObject->position.y = y;
model->anchorObject->immobile = TRUE;
}
static void
modelAddEdgeAnchors (Model *model,
int x,
int y,
int width,
int height)
{
Object *o;
o = &model->objects[0];
o->position.x = x;
o->position.y = y;
o->immobile = TRUE;
o = &model->objects[GRID_WIDTH - 1];
o->position.x = x + width;
o->position.y = y;
o->immobile = TRUE;
o = &model->objects[GRID_WIDTH * (GRID_HEIGHT - 1)];
o->position.x = x;
o->position.y = y + height;
o->immobile = TRUE;
o = &model->objects[model->numObjects - 1];
o->position.x = x + width;
o->position.y = y + height;
o->immobile = TRUE;
if (!model->anchorObject)
model->anchorObject = &model->objects[0];
}
static void
modelRemoveEdgeAnchors (Model *model,
int x,
int y,
int width,
int height)
{
Object *o;
o = &model->objects[0];
o->position.x = x;
o->position.y = y;
if (o != model->anchorObject)
o->immobile = FALSE;
o = &model->objects[GRID_WIDTH - 1];
o->position.x = x + width;
o->position.y = y;
if (o != model->anchorObject)
o->immobile = FALSE;
o = &model->objects[GRID_WIDTH * (GRID_HEIGHT - 1)];
o->position.x = x;
o->position.y = y + height;
if (o != model->anchorObject)
o->immobile = FALSE;
o = &model->objects[model->numObjects - 1];
o->position.x = x + width;
o->position.y = y + height;
if (o != model->anchorObject)
o->immobile = FALSE;
}
static void
modelAdjustObjectPosition (Model *model,
Object *object,
int x,
int y,
int width,
int height)
{
Object *o;
int gridX, gridY, i = 0;
for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
{
for (gridX = 0; gridX < GRID_WIDTH; gridX++)
{
o = &model->objects[i];
if (o == object)
{
o->position.x = x + (gridX * width) / (GRID_WIDTH - 1);
o->position.y = y + (gridY * height) / (GRID_HEIGHT - 1);
return;
}
i++;
}
}
}
static void
modelInitObjects (Model *model,
int x,
int y,
int width,
int height)
{
int gridX, gridY, i = 0;
float gw, gh;
gw = GRID_WIDTH - 1;
gh = GRID_HEIGHT - 1;
for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
{
for (gridX = 0; gridX < GRID_WIDTH; gridX++)
{
objectInit (&model->objects[i],
x + (gridX * width) / gw,
y + (gridY * height) / gh,
0, 0);
i++;
}
}
modelSetMiddleAnchor (model, x, y, width, height);
}
static void
modelUpdateSnapping (CompWindow *window,
Model *model)
{
unsigned int edgeMask, gridMask, mask;
int gridX, gridY, i = 0;
edgeMask = model->edgeMask;
if (model->snapCnt[NORTH])
edgeMask &= ~SouthEdgeMask;
else if (model->snapCnt[SOUTH])
edgeMask &= ~NorthEdgeMask;
if (model->snapCnt[WEST])
edgeMask &= ~EastEdgeMask;
else if (model->snapCnt[EAST])
edgeMask &= ~WestEdgeMask;
for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
{
if (gridY == 0)
gridMask = edgeMask & NorthEdgeMask;
else if (gridY == GRID_HEIGHT - 1)
gridMask = edgeMask & SouthEdgeMask;
else
gridMask = 0;
for (gridX = 0; gridX < GRID_WIDTH; gridX++)
{
mask = gridMask;
if (gridX == 0)
mask |= edgeMask & WestEdgeMask;
else if (gridX == GRID_WIDTH - 1)
mask |= edgeMask & EastEdgeMask;
if (mask != model->objects[i].edgeMask)
{
model->objects[i].edgeMask = mask;
if (mask & WestEdgeMask)
{
if (!model->objects[i].vertEdge.snapped)
findNextWestEdge (window, &model->objects[i]);
}
else if (mask & EastEdgeMask)
{
if (!model->objects[i].vertEdge.snapped)
findNextEastEdge (window, &model->objects[i]);
}
else
model->objects[i].vertEdge.snapped = FALSE;
if (mask & NorthEdgeMask)
{
if (!model->objects[i].horzEdge.snapped)
findNextNorthEdge (window, &model->objects[i]);
}
else if (mask & SouthEdgeMask)
{
if (!model->objects[i].horzEdge.snapped)
findNextSouthEdge (window, &model->objects[i]);
}
else
model->objects[i].horzEdge.snapped = FALSE;
}
i++;
}
}
}
static void
modelReduceEdgeEscapeVelocity (Model *model)
{
int gridX, gridY, i = 0;
for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
{
for (gridX = 0; gridX < GRID_WIDTH; gridX++)
{
if (model->objects[i].vertEdge.snapped)
model->objects[i].vertEdge.velocity *= drand48 () * 0.25f;
if (model->objects[i].horzEdge.snapped)
model->objects[i].horzEdge.velocity *= drand48 () * 0.25f;
i++;
}
}
}
static Bool
modelDisableSnapping (CompWindow *window,
Model *model)
{
int gridX, gridY, i = 0;
Bool snapped = FALSE;
for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
{
for (gridX = 0; gridX < GRID_WIDTH; gridX++)
{
if (model->objects[i].vertEdge.snapped ||
model->objects[i].horzEdge.snapped)
snapped = TRUE;
model->objects[i].vertEdge.snapped = FALSE;
model->objects[i].horzEdge.snapped = FALSE;
model->objects[i].edgeMask = 0;
i++;
}
}
memset (model->snapCnt, 0, sizeof (model->snapCnt));
return snapped;
}
static void
modelAdjustObjectsForShiver (Model *model,
int x,
int y,
int width,
int height)
{
int gridX, gridY, i = 0;
float vX, vY;
float w, h;
float scale;
w = width;
h = height;
for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
{
for (gridX = 0; gridX < GRID_WIDTH; gridX++)
{
if (!model->objects[i].immobile)
{
vX = model->objects[i].position.x - (x + w / 2);
vY = model->objects[i].position.y - (y + h / 2);
vX /= w;
vY /= h;
scale = ((float) rand () * 7.5f) / RAND_MAX;
model->objects[i].velocity.x += vX * scale;
model->objects[i].velocity.y += vY * scale;
}
i++;
}
}
}
static void
modelInitSprings (Model *model,
int x,
int y,
int width,
int height)
{
int gridX, gridY, i = 0;
float hpad, vpad;
model->numSprings = 0;
hpad = ((float) width) / (GRID_WIDTH - 1);
vpad = ((float) height) / (GRID_HEIGHT - 1);
for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
{
for (gridX = 0; gridX < GRID_WIDTH; gridX++)
{
if (gridX > 0)
modelAddSpring (model,
&model->objects[i - 1],
&model->objects[i],
hpad, 0);
if (gridY > 0)
modelAddSpring (model,
&model->objects[i - GRID_WIDTH],
&model->objects[i],
0, vpad);
i++;
}
}
}
static void
modelMove (Model *model,
float tx,
float ty)
{
int i;
for (i = 0; i < model->numObjects; i++)
{
model->objects[i].position.x += tx;
model->objects[i].position.y += ty;
}
}
static Model *
createModel (int x,
int y,
int width,
int height,
unsigned int edgeMask)
{
Model *model;
model = malloc (sizeof (Model));
if (!model)
return 0;
model->numObjects = GRID_WIDTH * GRID_HEIGHT;
model->objects = malloc (sizeof (Object) * model->numObjects);
if (!model->objects)
{
free (model);
return 0;
}
model->anchorObject = 0;
model->numSprings = 0;
model->steps = 0;
memset (model->snapCnt, 0, sizeof (model->snapCnt));
model->edgeMask = edgeMask;
modelInitObjects (model, x, y, width, height);
modelInitSprings (model, x, y, width, height);
modelCalcBounds (model);
return model;
}
static void
objectApplyForce (Object *object,
float fx,
float fy)
{
object->force.x += fx;
object->force.y += fy;
}
static void
springExertForces (Spring *spring,
float k)
{
Vector da, db;
Vector a, b;
a = spring->a->position;
b = spring->b->position;
da.x = 0.5f * (b.x - a.x - spring->offset.x);
da.y = 0.5f * (b.y - a.y - spring->offset.y);
db.x = 0.5f * (a.x - b.x + spring->offset.x);
db.y = 0.5f * (a.y - b.y + spring->offset.y);
objectApplyForce (spring->a, k * da.x, k * da.y);
objectApplyForce (spring->b, k * db.x, k * db.y);
}
static Bool
objectReleaseWestEdge (CompWindow *w,
Model *model,
Object *object)
{
if (fabs (object->velocity.x) > object->vertEdge.velocity)
{
object->position.x += object->velocity.x * 2.0f;
model->snapCnt[WEST]--;
object->vertEdge.snapped = FALSE;
object->edgeMask = 0;
modelUpdateSnapping (w, model);
return TRUE;
}
object->velocity.x = 0.0f;
return FALSE;
}
static Bool
objectReleaseEastEdge (CompWindow *w,
Model *model,
Object *object)
{
if (fabs (object->velocity.x) > object->vertEdge.velocity)
{
object->position.x += object->velocity.x * 2.0f;
model->snapCnt[EAST]--;
object->vertEdge.snapped = FALSE;
object->edgeMask = 0;
modelUpdateSnapping (w, model);
return TRUE;
}
object->velocity.x = 0.0f;
return FALSE;
}
static Bool
objectReleaseNorthEdge (CompWindow *w,
Model *model,
Object *object)
{
if (fabs (object->velocity.y) > object->horzEdge.velocity)
{
object->position.y += object->velocity.y * 2.0f;
model->snapCnt[NORTH]--;
object->horzEdge.snapped = FALSE;
object->edgeMask = 0;
modelUpdateSnapping (w, model);
return TRUE;
}
object->velocity.y = 0.0f;
return FALSE;
}
static Bool
objectReleaseSouthEdge (CompWindow *w,
Model *model,
Object *object)
{
if (fabs (object->velocity.y) > object->horzEdge.velocity)
{
object->position.y += object->velocity.y * 2.0f;
model->snapCnt[SOUTH]--;
object->horzEdge.snapped = FALSE;
object->edgeMask = 0;
modelUpdateSnapping (w, model);
return TRUE;
}
object->velocity.y = 0.0f;
return FALSE;
}
static float
modelStepObject (CompWindow *window,
Model *model,
Object *object,
float friction,
float *force)
{
object->theta += 0.05f;
if (object->immobile)
{
object->velocity.x = 0.0f;
object->velocity.y = 0.0f;
object->force.x = 0.0f;
object->force.y = 0.0f;
*force = 0.0f;
return 0.0f;
}
else
{
object->force.x -= friction * object->velocity.x;
object->force.y -= friction * object->velocity.y;
object->velocity.x += object->force.x / MASS;
object->velocity.y += object->force.y / MASS;
if (object->edgeMask)
{
if (object->edgeMask & WestEdgeMask)
{
if (object->position.y < object->vertEdge.start ||
object->position.y > object->vertEdge.end)
findNextWestEdge (window, object);
if (!object->vertEdge.snapped ||
objectReleaseWestEdge (window, model, object))
{
object->position.x += object->velocity.x;
if (object->velocity.x < 0.0f &&
object->position.x < object->vertEdge.attract)
{
if (object->position.x < object->vertEdge.next)
{
object->vertEdge.snapped = TRUE;
object->position.x = object->vertEdge.next;
object->velocity.x = 0.0f;
model->snapCnt[WEST]++;
modelUpdateSnapping (window, model);
}
else
{
object->velocity.x -=
object->vertEdge.attract - object->position.x;
}
}
if (object->position.x > object->vertEdge.prev)
findNextWestEdge (window, object);
}
}
else if (object->edgeMask & EastEdgeMask)
{
if (object->position.y < object->vertEdge.start ||
object->position.y > object->vertEdge.end)
findNextEastEdge (window, object);
if (!object->vertEdge.snapped ||
objectReleaseEastEdge (window, model, object))
{
object->position.x += object->velocity.x;
if (object->velocity.x > 0.0f &&
object->position.x > object->vertEdge.attract)
{
if (object->position.x > object->vertEdge.next)
{
object->vertEdge.snapped = TRUE;
object->position.x = object->vertEdge.next;
object->velocity.x = 0.0f;
model->snapCnt[EAST]++;
modelUpdateSnapping (window, model);
}
else
{
object->velocity.x =
object->position.x - object->vertEdge.attract;
}
}
if (object->position.x < object->vertEdge.prev)
findNextEastEdge (window, object);
}
}
else
object->position.x += object->velocity.x;
if (object->edgeMask & NorthEdgeMask)
{
if (object->position.x < object->horzEdge.start ||
object->position.x > object->horzEdge.end)
findNextNorthEdge (window, object);
if (!object->horzEdge.snapped ||
objectReleaseNorthEdge (window, model, object))
{
object->position.y += object->velocity.y;
if (object->velocity.y < 0.0f &&
object->position.y < object->horzEdge.attract)
{
if (object->position.y < object->horzEdge.next)
{
object->horzEdge.snapped = TRUE;
object->position.y = object->horzEdge.next;
object->velocity.y = 0.0f;
model->snapCnt[NORTH]++;
modelUpdateSnapping (window, model);
}
else
{
object->velocity.y -=
object->horzEdge.attract - object->position.y;
}
}
if (object->position.y > object->horzEdge.prev)
findNextNorthEdge (window, object);
}
}
else if (object->edgeMask & SouthEdgeMask)
{
if (object->position.x < object->horzEdge.start ||
object->position.x > object->horzEdge.end)
findNextSouthEdge (window, object);
if (!object->horzEdge.snapped ||
objectReleaseSouthEdge (window, model, object))
{
object->position.y += object->velocity.y;
if (object->velocity.y > 0.0f &&
object->position.y > object->horzEdge.attract)
{
if (object->position.y > object->horzEdge.next)
{
object->horzEdge.snapped = TRUE;
object->position.y = object->horzEdge.next;
object->velocity.y = 0.0f;
model->snapCnt[SOUTH]++;
modelUpdateSnapping (window, model);
}
else
{
object->velocity.y =
object->position.y - object->horzEdge.attract;
}
}
if (object->position.y < object->horzEdge.prev)
findNextSouthEdge (window, object);
}
}
else
object->position.y += object->velocity.y;
}
else
{
object->position.x += object->velocity.x;
object->position.y += object->velocity.y;
}
*force = fabs (object->force.x) + fabs (object->force.y);
object->force.x = 0.0f;
object->force.y = 0.0f;
return fabs (object->velocity.x) + fabs (object->velocity.y);
}
}
static int
modelStep (CompWindow *window,
Model *model,
float friction,
float k,
float time)
{
int i, j, steps, wobbly = 0;
float velocitySum = 0.0f;
float force, forceSum = 0.0f;
model->steps += time / 15.0f;
steps = floor (model->steps);
model->steps -= steps;
if (!steps)
return TRUE;
for (j = 0; j < steps; j++)
{
for (i = 0; i < model->numSprings; i++)
springExertForces (&model->springs[i], k);
for (i = 0; i < model->numObjects; i++)
{
velocitySum += modelStepObject (window,
model,
&model->objects[i],
friction,
&force);
forceSum += force;
}
}
modelCalcBounds (model);
if (velocitySum > 0.5f)
wobbly |= WobblyVelocity;
if (forceSum > 20.0f)
wobbly |= WobblyForce;
return wobbly;
}
static void
bezierPatchEvaluate (Model *model,
float u,
float v,
float *patchX,
float *patchY)
{
float coeffsU[4], coeffsV[4];
float x, y;
int i, j;
coeffsU[0] = (1 - u) * (1 - u) * (1 - u);
coeffsU[1] = 3 * u * (1 - u) * (1 - u);
coeffsU[2] = 3 * u * u * (1 - u);
coeffsU[3] = u * u * u;
coeffsV[0] = (1 - v) * (1 - v) * (1 - v);
coeffsV[1] = 3 * v * (1 - v) * (1 - v);
coeffsV[2] = 3 * v * v * (1 - v);
coeffsV[3] = v * v * v;
x = y = 0.0f;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
x += coeffsU[i] * coeffsV[j] *
model->objects[j * GRID_WIDTH + i].position.x;
y += coeffsU[i] * coeffsV[j] *
model->objects[j * GRID_WIDTH + i].position.y;
}
}
*patchX = x;
*patchY = y;
}
static Bool
wobblyEnsureModel (CompWindow *w)
{
WOBBLY_WINDOW (w);
if (!ww->model)
{
unsigned int edgeMask = 0;
if (w->type & CompWindowTypeNormalMask)
edgeMask = WestEdgeMask | EastEdgeMask | NorthEdgeMask |
SouthEdgeMask;
ww->model = createModel (WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w),
edgeMask);
if (!ww->model)
return FALSE;
}
return TRUE;
}
static float
objectDistance (Object *object,
float x,
float y)
{
float dx, dy;
dx = object->position.x - x;
dy = object->position.y - y;
return sqrt (dx * dx + dy * dy);
}
static Object *
modelFindNearestObject (Model *model,
float x,
float y)
{
Object *object = &model->objects[0];
float distance, minDistance = 0.0;
int i;
for (i = 0; i < model->numObjects; i++)
{
distance = objectDistance (&model->objects[i], x, y);
if (i == 0 || distance < minDistance)
{
minDistance = distance;
object = &model->objects[i];
}
}
return object;
}
static Bool
isWobblyWin (CompWindow *w)
{
WOBBLY_WINDOW (w);
if (ww->model)
return TRUE;
/* avoid tiny windows */
if (w->width == 1 && w->height == 1)
return FALSE;
/* avoid fullscreen windows */
if (w->attrib.x <= 0 &&
w->attrib.y <= 0 &&
w->attrib.x + w->width >= w->screen->width &&
w->attrib.y + w->height >= w->screen->height)
return FALSE;
return TRUE;
}
static void
wobblyPreparePaintScreen (CompScreen *s,
int msSinceLastPaint)
{
WobblyWindow *ww;
CompWindow *w;
WOBBLY_SCREEN (s);
if (ws->wobblyWindows & (WobblyInitial | WobblyVelocity))
{
BoxRec box;
Point topLeft, bottomRight;
float friction, springK;
Model *model;
friction = ws->opt[WOBBLY_SCREEN_OPTION_FRICTION].value.f;
springK = ws->opt[WOBBLY_SCREEN_OPTION_SPRING_K].value.f;
ws->wobblyWindows = 0;
for (w = s->windows; w; w = w->next)
{
ww = GET_WOBBLY_WINDOW (w, ws);
if (ww->wobbly)
{
if (ww->wobbly & (WobblyInitial | WobblyVelocity))
{
model = ww->model;
topLeft = model->topLeft;
bottomRight = model->bottomRight;
ww->wobbly = modelStep (w, model, friction, springK,
(ww->wobbly & WobblyVelocity) ?
msSinceLastPaint :
s->redrawTime);
if ((ww->state & MAXIMIZE_STATE) && ww->grabbed)
ww->wobbly |= WobblyForce;
if (ww->wobbly)
{
WOBBLY_DISPLAY (s->display);
/* snapped to more than one edge, we have to reduce
edge escape velocity until only one edge is snapped */
if (ww->wobbly == WobblyForce && !ww->grabbed)
{
modelReduceEdgeEscapeVelocity (ww->model);
ww->wobbly |= WobblyInitial;
}
if (!ww->grabbed && wd->yConstrained)
{
float bottommostYPos = MINSHORT;
int i;
/* find the bottommost top-row object */
for (i = 0; i < GRID_WIDTH; i++)
{
if (model->objects[i].position.y >
bottommostYPos)
bottommostYPos =
model->objects[i].position.y;
}
int decorTop = bottommostYPos +
w->output.top - w->input.top;
if (ws->grabWindowWorkArea->y > decorTop)
{
/* constrain to work area */
modelMove (model, 0,
ws->grabWindowWorkArea->y -
decorTop);
modelCalcBounds (model);
}
}
}
else
{
ww->model = 0;
if (w->attrib.x == w->serverX &&
w->attrib.y == w->serverY)
{
moveWindow (w,
model->topLeft.x + w->output.left -
w->attrib.x,
model->topLeft.y + w->output.top -
w->attrib.y,
TRUE, TRUE);
syncWindowPosition (w);
}
ww->model = model;
}
if (!(s->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK))
{
if (ww->wobbly)
{
if (ww->model->topLeft.x < topLeft.x)
topLeft.x = ww->model->topLeft.x;
if (ww->model->topLeft.y < topLeft.y)
topLeft.y = ww->model->topLeft.y;
if (ww->model->bottomRight.x > bottomRight.x)
bottomRight.x = ww->model->bottomRight.x;
if (ww->model->bottomRight.y > bottomRight.y)
bottomRight.y = ww->model->bottomRight.y;
}
else
addWindowDamage (w);
box.x1 = topLeft.x;
box.y1 = topLeft.y;
box.x2 = bottomRight.x + 0.5f;
box.y2 = bottomRight.y + 0.5f;
box.x1 -= w->attrib.x + w->attrib.border_width;
box.y1 -= w->attrib.y + w->attrib.border_width;
box.x2 -= w->attrib.x + w->attrib.border_width;
box.y2 -= w->attrib.y + w->attrib.border_width;
addWindowDamageRect (w, &box);
}
}
ws->wobblyWindows |= ww->wobbly;
}
}
}
UNWRAP (ws, s, preparePaintScreen);
(*s->preparePaintScreen) (s, msSinceLastPaint);
WRAP (ws, s, preparePaintScreen, wobblyPreparePaintScreen);
}
static void
wobblyDonePaintScreen (CompScreen *s)
{
WOBBLY_SCREEN (s);
if (ws->wobblyWindows & (WobblyVelocity | WobblyInitial))
damagePendingOnScreen (s);
UNWRAP (ws, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen);
}
static void
wobblyDrawWindowGeometry (CompWindow *w)
{
int texUnit = w->texUnits;
int currentTexUnit = 0;
int stride = w->vertexStride;
GLfloat *vertices = w->vertices + (stride - 3);
stride *= sizeof (GLfloat);
glVertexPointer (3, GL_FLOAT, stride, vertices);
while (texUnit--)
{
if (texUnit != currentTexUnit)
{
w->screen->clientActiveTexture (GL_TEXTURE0_ARB + texUnit);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
currentTexUnit = texUnit;
}
vertices -= w->texCoordSize;
glTexCoordPointer (w->texCoordSize, GL_FLOAT, stride, vertices);
}
glDrawElements (GL_QUADS, w->indexCount, GL_UNSIGNED_SHORT, w->indices);
/* disable all texture coordinate arrays except 0 */
texUnit = w->texUnits;
if (texUnit > 1)
{
while (--texUnit)
{
(*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB + texUnit);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
}
(*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB);
}
}
static void
wobblyAddWindowGeometry (CompWindow *w,
CompMatrix *matrix,
int nMatrix,
Region region,
Region clip)
{
WOBBLY_WINDOW (w);
WOBBLY_SCREEN (w->screen);
if (ww->wobbly)
{
BoxPtr pClip;
int nClip, nVertices, nIndices;
GLushort *i;
GLfloat *v;
int x1, y1, x2, y2;
float width, height;
float deformedX, deformedY;
int x, y, iw, ih, wx, wy;
int vSize, it;
int gridW, gridH;
Bool rect = TRUE;
for (it = 0; it < nMatrix; it++)
{
if (matrix[it].xy != 0.0f || matrix[it].yx != 0.0f)
{
rect = FALSE;
break;
}
}
wx = WIN_X (w);
wy = WIN_Y (w);
width = WIN_W (w);
height = WIN_H (w);
gridW = width / ws->opt[WOBBLY_SCREEN_OPTION_GRID_RESOLUTION].value.i;
if (gridW < ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i)
gridW = ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i;
gridH = height / ws->opt[WOBBLY_SCREEN_OPTION_GRID_RESOLUTION].value.i;
if (gridH < ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i)
gridH = ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE].value.i;
nClip = region->numRects;
pClip = region->rects;
w->texUnits = nMatrix;
vSize = 3 + nMatrix * 2;
nVertices = w->vCount;
nIndices = w->indexCount;
v = w->vertices + (nVertices * vSize);
i = w->indices + nIndices;
while (nClip--)
{
x1 = pClip->x1;
y1 = pClip->y1;
x2 = pClip->x2;
y2 = pClip->y2;
iw = ((x2 - x1 - 1) / gridW) + 1;
ih = ((y2 - y1 - 1) / gridH) + 1;
if (nIndices + (iw * ih * 4) > w->indexSize)
{
if (!moreWindowIndices (w, nIndices + (iw * ih * 4)))
return;
i = w->indices + nIndices;
}
iw++;
ih++;
for (y = 0; y < ih - 1; y++)
{
for (x = 0; x < iw - 1; x++)
{
*i++ = nVertices + iw * (y + 1) + x;
*i++ = nVertices + iw * (y + 1) + x + 1;
*i++ = nVertices + iw * y + x + 1;
*i++ = nVertices + iw * y + x;
nIndices += 4;
}
}
if (((nVertices + iw * ih) * vSize) > w->vertexSize)
{
if (!moreWindowVertices (w, (nVertices + iw * ih) * vSize))
return;
v = w->vertices + (nVertices * vSize);
}
for (y = y1;; y += gridH)
{
if (y > y2)
y = y2;
for (x = x1;; x += gridW)
{
if (x > x2)
x = x2;
bezierPatchEvaluate (ww->model,
(x - wx) / width,
(y - wy) / height,
&deformedX,
&deformedY);
if (rect)
{
for (it = 0; it < nMatrix; it++)
{
*v++ = COMP_TEX_COORD_X (&matrix[it], x);
*v++ = COMP_TEX_COORD_Y (&matrix[it], y);
}
}
else
{
for (it = 0; it < nMatrix; it++)
{
*v++ = COMP_TEX_COORD_XY (&matrix[it], x, y);
*v++ = COMP_TEX_COORD_YX (&matrix[it], x, y);
}
}
*v++ = deformedX;
*v++ = deformedY;
*v++ = 0.0;
nVertices++;
if (x == x2)
break;
}
if (y == y2)
break;
}
pClip++;
}
w->vCount = nVertices;
w->vertexStride = vSize;
w->texCoordSize = 2;
w->indexCount = nIndices;
w->drawWindowGeometry = wobblyDrawWindowGeometry;
}
else
{
UNWRAP (ws, w->screen, addWindowGeometry);
(*w->screen->addWindowGeometry) (w, matrix, nMatrix, region, clip);
WRAP (ws, w->screen, addWindowGeometry, wobblyAddWindowGeometry);
}
}
static Bool
wobblyPaintWindow (CompWindow *w,
const WindowPaintAttrib *attrib,
const CompTransform *transform,
Region region,
unsigned int mask)
{
Bool status;
WOBBLY_SCREEN (w->screen);
WOBBLY_WINDOW (w);
if (ww->wobbly)
mask |= PAINT_WINDOW_TRANSFORMED_MASK;
UNWRAP (ws, w->screen, paintWindow);
status = (*w->screen->paintWindow) (w, attrib, transform, region, mask);
WRAP (ws, w->screen, paintWindow, wobblyPaintWindow);
return status;
}
static Bool
wobblyEnableSnapping (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
CompScreen *s;
CompWindow *w;
WOBBLY_DISPLAY (d);
for (s = d->screens; s; s = s->next)
{
for (w = s->windows; w; w = w->next)
{
WOBBLY_WINDOW (w);
if (ww->grabbed && ww->model)
modelUpdateSnapping (w, ww->model);
}
}
wd->snapping = TRUE;
return FALSE;
}
static Bool
wobblyDisableSnapping (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
CompScreen *s;
CompWindow *w;
WOBBLY_DISPLAY (d);
if (!wd->snapping)
return FALSE;
for (s = d->screens; s; s = s->next)
{
for (w = s->windows; w; w = w->next)
{
WOBBLY_WINDOW (w);
if (ww->grabbed && ww->model)
{
if (modelDisableSnapping (w, ww->model))
{
WOBBLY_SCREEN (w->screen);
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
}
}
}
wd->snapping = FALSE;
return FALSE;
}
static Bool
wobblyShiver (CompDisplay *d,
CompAction *action,
CompActionState state,
CompOption *option,
int nOption)
{
CompWindow *w;
Window xid;
xid = getIntOptionNamed (option, nOption, "window", 0);
w = findWindowAtDisplay (d, xid);
if (w && isWobblyWin (w) && wobblyEnsureModel (w))
{
WOBBLY_SCREEN (w->screen);
WOBBLY_WINDOW (w);
modelSetMiddleAnchor (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
modelAdjustObjectsForShiver (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
return FALSE;
}
static void
wobblyHandleEvent (CompDisplay *d,
XEvent *event)
{
Window activeWindow = d->activeWindow;
CompWindow *w;
CompScreen *s;
WOBBLY_DISPLAY (d);
switch (event->type) {
case MapNotify:
w = findWindowAtDisplay (d, event->xmap.window);
if (w)
{
WOBBLY_WINDOW (w);
if (ww->model)
{
modelInitObjects (ww->model,
WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
modelInitSprings (ww->model,
WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
}
}
break;
default:
if (event->type == d->xkbEvent)
{
XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
if (xkbEvent->xkb_type == XkbStateNotify)
{
XkbStateNotifyEvent *stateEvent = (XkbStateNotifyEvent *) event;
CompAction *action;
Bool inverted;
unsigned int mods = 0xffffffff;
action =
&wd->opt[WOBBLY_DISPLAY_OPTION_SNAP_KEY].value.action;
inverted = wd->opt[WOBBLY_DISPLAY_OPTION_SNAP_INVERTED].value.b;
if (action->type & CompBindingTypeKey)
mods = action->key.modifiers;
if ((stateEvent->mods & mods) == mods)
{
if (inverted)
wobblyDisableSnapping (d, NULL, 0, NULL, 0);
else
wobblyEnableSnapping (d, NULL, 0, NULL, 0);
}
else
{
if (inverted)
wobblyEnableSnapping (d, NULL, 0, NULL, 0);
else
wobblyDisableSnapping (d, NULL, 0, NULL, 0);
}
}
}
break;
}
UNWRAP (wd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (wd, d, handleEvent, wobblyHandleEvent);
switch (event->type) {
case MotionNotify:
s = findScreenAtDisplay (d, event->xmotion.root);
if (s)
{
WOBBLY_SCREEN (s);
if (ws->grabWindow &&
ws->moveWindow &&
ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
{
WOBBLY_WINDOW (ws->grabWindow);
if (ww->state & MAXIMIZE_STATE)
{
if (ww->model && ww->grabbed)
{
int dx, dy;
if (ww->state & CompWindowStateMaximizedHorzMask)
dx = pointerX - lastPointerX;
else
dx = 0;
if (ww->state & CompWindowStateMaximizedVertMask)
dy = pointerY - lastPointerY;
else
dy = 0;
ww->model->anchorObject->position.x += dx;
ww->model->anchorObject->position.y += dy;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (s);
}
}
}
}
default:
break;
}
if (d->activeWindow != activeWindow)
{
w = findWindowAtDisplay (d, d->activeWindow);
if (w && isWobblyWin (w))
{
int mIndex;
int focusEffect;
WOBBLY_WINDOW (w);
WOBBLY_SCREEN (w->screen);
mIndex = WOBBLY_SCREEN_OPTION_FOCUS_WINDOW_MATCH;
focusEffect = ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_EFFECT].value.i;
if ((focusEffect != WOBBLY_EFFECT_NONE) &&
matchEval (&ws->opt[mIndex].value.match, w) &&
wobblyEnsureModel (w))
{
switch (focusEffect) {
case WOBBLY_EFFECT_SHIVER:
modelAdjustObjectsForShiver (ww->model,
WIN_X (w),
WIN_Y (w),
WIN_W (w),
WIN_H (w));
default:
break;
}
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
}
}
}
static Bool
wobblyDamageWindowRect (CompWindow *w,
Bool initial,
BoxPtr rect)
{
Bool status;
WOBBLY_SCREEN (w->screen);
if (!initial)
{
WOBBLY_WINDOW (w);
if (ww->wobbly == WobblyForce)
{
REGION region;
region.rects = &region.extents;
region.numRects = region.size = 1;
region.extents.x1 = ww->model->topLeft.x;
region.extents.y1 = ww->model->topLeft.y;
region.extents.x2 = ww->model->bottomRight.x + 0.5f;
region.extents.y2 = ww->model->bottomRight.y + 0.5f;
damageScreenRegion (w->screen, &region);
return TRUE;
}
}
UNWRAP (ws, w->screen, damageWindowRect);
status = (*w->screen->damageWindowRect) (w, initial, rect);
WRAP (ws, w->screen, damageWindowRect, wobblyDamageWindowRect);
if (initial)
{
if (isWobblyWin (w))
{
int mIndex;
int mapEffect;
WOBBLY_WINDOW (w);
WOBBLY_SCREEN (w->screen);
mIndex = WOBBLY_SCREEN_OPTION_MAP_WINDOW_MATCH;
mapEffect = ws->opt[WOBBLY_SCREEN_OPTION_MAP_EFFECT].value.i;
if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
wobblyEnsureModel (w);
if ((mapEffect != WOBBLY_EFFECT_NONE) &&
matchEval (&ws->opt[mIndex].value.match, w) &&
wobblyEnsureModel (w))
{
switch (mapEffect) {
case WOBBLY_EFFECT_SHIVER:
modelAdjustObjectsForShiver (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
default:
break;
}
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
}
}
return status;
}
static void
wobblyWindowResizeNotify (CompWindow *w,
int dx,
int dy,
int dwidth,
int dheight)
{
WOBBLY_SCREEN (w->screen);
WOBBLY_WINDOW (w);
if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b &&
isWobblyWin (w) &&
/* prevent wobbling when shading maximized windows - assuming that
the height difference shaded - non-shaded will hardly be -1 and
a lack of wobbly animation in that corner case is tolerable */
(dheight != -1) &&
((w->state | ww->state) & MAXIMIZE_STATE))
{
ww->state &= ~MAXIMIZE_STATE;
ww->state |= w->state & MAXIMIZE_STATE;
if (wobblyEnsureModel (w))
{
if (w->state & MAXIMIZE_STATE)
{
if (!ww->grabbed && ww->model->anchorObject)
{
ww->model->anchorObject->immobile = FALSE;
ww->model->anchorObject = NULL;
}
modelAddEdgeAnchors (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
}
else
{
modelRemoveEdgeAnchors (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
modelSetMiddleAnchor (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
}
modelInitSprings (ww->model,
WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
}
else if (ww->model)
{
if (ww->wobbly)
{
if (!(ww->state & MAXIMIZE_STATE))
modelSetTopAnchor (ww->model, WIN_X (w), WIN_Y (w), WIN_W (w));
}
else
{
modelInitObjects (ww->model,
WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
}
modelInitSprings (ww->model,
WIN_X (w), WIN_Y (w), WIN_W (w), WIN_H (w));
}
/* update grab */
if (ww->model && ww->grabbed)
{
if (ww->model->anchorObject)
ww->model->anchorObject->immobile = FALSE;
ww->model->anchorObject = modelFindNearestObject (ww->model,
pointerX,
pointerY);
ww->model->anchorObject->immobile = TRUE;
modelAdjustObjectPosition (ww->model,
ww->model->anchorObject,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
}
UNWRAP (ws, w->screen, windowResizeNotify);
(*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
WRAP (ws, w->screen, windowResizeNotify, wobblyWindowResizeNotify);
}
static void
wobblyWindowMoveNotify (CompWindow *w,
int dx,
int dy,
Bool immediate)
{
WOBBLY_SCREEN (w->screen);
WOBBLY_WINDOW (w);
if (ww->model)
{
if (ww->grabbed && !immediate)
{
if (ww->state & MAXIMIZE_STATE)
{
int i;
for (i = 0; i < ww->model->numObjects; i++)
{
if (ww->model->objects[i].immobile)
{
ww->model->objects[i].position.x += dx;
ww->model->objects[i].position.y += dy;
}
}
}
else
{
ww->model->anchorObject->position.x += dx;
ww->model->anchorObject->position.y += dy;
}
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
else
modelMove (ww->model, dx, dy);
}
UNWRAP (ws, w->screen, windowMoveNotify);
(*w->screen->windowMoveNotify) (w, dx, dy, immediate);
WRAP (ws, w->screen, windowMoveNotify, wobblyWindowMoveNotify);
}
static void
wobblyWindowGrabNotify (CompWindow *w,
int x,
int y,
unsigned int state,
unsigned int mask)
{
int mIndex;
WOBBLY_SCREEN (w->screen);
mIndex = WOBBLY_SCREEN_OPTION_MOVE_WINDOW_MATCH;
if (!ws->grabWindow)
{
ws->grabMask = mask;
ws->grabWindow = w;
}
ws->moveWindow = FALSE;
if ((mask & CompWindowGrabButtonMask) &&
matchEval (&ws->opt[mIndex].value.match, w) &&
isWobblyWin (w))
{
WOBBLY_WINDOW (w);
ws->moveWindow = TRUE;
if (wobblyEnsureModel (w))
{
Spring *s;
int i;
CompPlugin *pMove;
WOBBLY_DISPLAY (w->screen->display);
if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
{
if (w->state & MAXIMIZE_STATE)
{
modelAddEdgeAnchors (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
}
else
{
modelRemoveEdgeAnchors (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
if (ww->model->anchorObject)
ww->model->anchorObject->immobile = FALSE;
}
}
else
{
if (ww->model->anchorObject)
ww->model->anchorObject->immobile = FALSE;
}
ww->model->anchorObject = modelFindNearestObject (ww->model, x, y);
ww->model->anchorObject->immobile = TRUE;
ww->grabbed = TRUE;
/* Update yConstrained and workArea at grab time */
wd->yConstrained = FALSE;
if (mask & CompWindowGrabExternalAppMask)
{
pMove = findActivePlugin ("move");
if (pMove && pMove->vTable->getObjectOptions)
{
int nOption = 0;
CompOption *moveOptions;
moveOptions = (*pMove->vTable->getObjectOptions)
(pMove, &core.displays->base, &nOption);
wd->yConstrained = getBoolOptionNamed (moveOptions, nOption,
"constrain_y", TRUE);
}
}
if (wd->yConstrained)
{
int output = outputDeviceForWindow (w);
ws->grabWindowWorkArea = &w->screen->outputDev[output].workArea;
}
if (mask & CompWindowGrabMoveMask)
{
modelDisableSnapping (w, ww->model);
if (wd->snapping)
modelUpdateSnapping (w, ww->model);
}
mIndex = WOBBLY_SCREEN_OPTION_GRAB_WINDOW_MATCH;
if (matchEval (&ws->opt[mIndex].value.match, w))
{
for (i = 0; i < ww->model->numSprings; i++)
{
s = &ww->model->springs[i];
if (s->a == ww->model->anchorObject)
{
s->b->velocity.x -= s->offset.x * 0.05f;
s->b->velocity.y -= s->offset.y * 0.05f;
}
else if (s->b == ww->model->anchorObject)
{
s->a->velocity.x += s->offset.x * 0.05f;
s->a->velocity.y += s->offset.y * 0.05f;
}
}
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
}
}
UNWRAP (ws, w->screen, windowGrabNotify);
(*w->screen->windowGrabNotify) (w, x, y, state, mask);
WRAP (ws, w->screen, windowGrabNotify, wobblyWindowGrabNotify);
}
static void
wobblyWindowUngrabNotify (CompWindow *w)
{
WOBBLY_SCREEN (w->screen);
WOBBLY_WINDOW (w);
if (w == ws->grabWindow)
{
ws->grabMask = 0;
ws->grabWindow = NULL;
}
if (ww->grabbed)
{
if (ww->model)
{
if (ww->model->anchorObject)
ww->model->anchorObject->immobile = FALSE;
ww->model->anchorObject = NULL;
if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
{
if (ww->state & MAXIMIZE_STATE)
modelAddEdgeAnchors (ww->model,
WIN_X (w), WIN_Y (w),
WIN_W (w), WIN_H (w));
}
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen (w->screen);
}
ww->grabbed = FALSE;
}
UNWRAP (ws, w->screen, windowUngrabNotify);
(*w->screen->windowUngrabNotify) (w);
WRAP (ws, w->screen, windowUngrabNotify, wobblyWindowUngrabNotify);
}
static Bool
wobblyPaintOutput (CompScreen *s,
const ScreenPaintAttrib *sAttrib,
const CompTransform *transform,
Region region,
CompOutput *output,
unsigned int mask)
{
Bool status;
WOBBLY_SCREEN (s);
if (ws->wobblyWindows)
mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
UNWRAP (ws, s, paintOutput);
status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
WRAP (ws, s, paintOutput, wobblyPaintOutput);
return status;
}
static CompOption *
wobblyGetDisplayOptions (CompPlugin *plugin,
CompDisplay *display,
int *count)
{
WOBBLY_DISPLAY (display);
*count = NUM_OPTIONS (wd);
return wd->opt;
}
static Bool
wobblySetDisplayOption (CompPlugin *plugin,
CompDisplay *display,
const char *name,
CompOptionValue *value)
{
CompOption *o;
int index;
WOBBLY_DISPLAY (display);
o = compFindOption (wd->opt, NUM_OPTIONS (wd), name, &index);
if (!o)
return FALSE;
switch (index) {
case WOBBLY_DISPLAY_OPTION_SNAP_INVERTED:
if (compSetBoolOption (o, value))
{
if (value->b)
wobblyEnableSnapping (display, NULL, 0, NULL, 0);
else
wobblyDisableSnapping (display, NULL, 0, NULL, 0);
return TRUE;
}
break;
case WOBBLY_DISPLAY_OPTION_SNAP_KEY:
/* ignore the key */
value->action.key.keycode = 0;
if (compSetActionOption (o, value))
return TRUE;
break;
default:
return compSetDisplayOption (display, o, value);
}
return FALSE;
}
static const CompMetadataOptionInfo wobblyDisplayOptionInfo[] = {
{ "snap_key", "key", "<passive_grab>false</passive_grab>",
wobblyEnableSnapping, wobblyDisableSnapping },
{ "snap_inverted", "bool", 0, 0, 0 },
{ "shiver", "bell", 0, wobblyShiver, 0 }
};
static Bool
wobblyInitDisplay (CompPlugin *p,
CompDisplay *d)
{
WobblyDisplay *wd;
if (!checkPluginABI ("core", CORE_ABIVERSION))
return FALSE;
wd = malloc (sizeof (WobblyDisplay));
if (!wd)
return FALSE;
if (!compInitDisplayOptionsFromMetadata (d,
&wobblyMetadata,
wobblyDisplayOptionInfo,
wd->opt,
WOBBLY_DISPLAY_OPTION_NUM))
{
free (wd);
return FALSE;
}
wd->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (wd->screenPrivateIndex < 0)
{
compFiniDisplayOptions (d, wd->opt, WOBBLY_DISPLAY_OPTION_NUM);
free (wd);
return FALSE;
}
WRAP (wd, d, handleEvent, wobblyHandleEvent);
wd->snapping = FALSE;
wd->yConstrained = FALSE;
d->base.privates[displayPrivateIndex].ptr = wd;
return TRUE;
}
static void
wobblyFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
WOBBLY_DISPLAY (d);
freeScreenPrivateIndex (d, wd->screenPrivateIndex);
UNWRAP (wd, d, handleEvent);
compFiniDisplayOptions (d, wd->opt, WOBBLY_DISPLAY_OPTION_NUM);
free (wd);
}
static Bool
wobblyInitScreen (CompPlugin *p,
CompScreen *s)
{
WobblyScreen *ws;
WOBBLY_DISPLAY (s->display);
ws = malloc (sizeof (WobblyScreen));
if (!ws)
return FALSE;
if (!compInitScreenOptionsFromMetadata (s,
&wobblyMetadata,
wobblyScreenOptionInfo,
ws->opt,
WOBBLY_SCREEN_OPTION_NUM))
{
free (ws);
return FALSE;
}
ws->windowPrivateIndex = allocateWindowPrivateIndex (s);
if (ws->windowPrivateIndex < 0)
{
compFiniScreenOptions (s, ws->opt, WOBBLY_SCREEN_OPTION_NUM);
free (ws);
return FALSE;
}
ws->wobblyWindows = FALSE;
ws->grabMask = 0;
ws->grabWindow = NULL;
ws->moveWindow = FALSE;
ws->grabWindowWorkArea = NULL;
WRAP (ws, s, preparePaintScreen, wobblyPreparePaintScreen);
WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen);
WRAP (ws, s, paintOutput, wobblyPaintOutput);
WRAP (ws, s, paintWindow, wobblyPaintWindow);
WRAP (ws, s, damageWindowRect, wobblyDamageWindowRect);
WRAP (ws, s, addWindowGeometry, wobblyAddWindowGeometry);
WRAP (ws, s, windowResizeNotify, wobblyWindowResizeNotify);
WRAP (ws, s, windowMoveNotify, wobblyWindowMoveNotify);
WRAP (ws, s, windowGrabNotify, wobblyWindowGrabNotify);
WRAP (ws, s, windowUngrabNotify, wobblyWindowUngrabNotify);
s->base.privates[wd->screenPrivateIndex].ptr = ws;
return TRUE;
}
static void
wobblyFiniScreen (CompPlugin *p,
CompScreen *s)
{
WOBBLY_SCREEN (s);
freeWindowPrivateIndex (s, ws->windowPrivateIndex);
UNWRAP (ws, s, preparePaintScreen);
UNWRAP (ws, s, donePaintScreen);
UNWRAP (ws, s, paintOutput);
UNWRAP (ws, s, paintWindow);
UNWRAP (ws, s, damageWindowRect);
UNWRAP (ws, s, addWindowGeometry);
UNWRAP (ws, s, windowResizeNotify);
UNWRAP (ws, s, windowMoveNotify);
UNWRAP (ws, s, windowGrabNotify);
UNWRAP (ws, s, windowUngrabNotify);
compFiniScreenOptions (s, ws->opt, WOBBLY_SCREEN_OPTION_NUM);
free (ws);
}
static Bool
wobblyInitWindow (CompPlugin *p,
CompWindow *w)
{
WobblyWindow *ww;
WOBBLY_SCREEN (w->screen);
ww = malloc (sizeof (WobblyWindow));
if (!ww)
return FALSE;
ww->model = 0;
ww->wobbly = 0;
ww->grabbed = FALSE;
ww->state = w->state;
w->base.privates[ws->windowPrivateIndex].ptr = ww;
if (w->mapNum && ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
{
if (isWobblyWin (w))
wobblyEnsureModel (w);
}
return TRUE;
}
static void
wobblyFiniWindow (CompPlugin *p,
CompWindow *w)
{
WOBBLY_WINDOW (w);
WOBBLY_SCREEN (w->screen);
if (ws->grabWindow == w)
{
ws->grabWindow = NULL;
ws->grabMask = 0;
}
if (ww->model)
{
free (ww->model->objects);
free (ww->model);
}
free (ww);
}
static CompBool
wobblyInitObject (CompPlugin *p,
CompObject *o)
{
static InitPluginObjectProc dispTab[] = {
(InitPluginObjectProc) 0, /* InitCore */
(InitPluginObjectProc) wobblyInitDisplay,
(InitPluginObjectProc) wobblyInitScreen,
(InitPluginObjectProc) wobblyInitWindow
};
RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
}
static void
wobblyFiniObject (CompPlugin *p,
CompObject *o)
{
static FiniPluginObjectProc dispTab[] = {
(FiniPluginObjectProc) 0, /* FiniCore */
(FiniPluginObjectProc) wobblyFiniDisplay,
(FiniPluginObjectProc) wobblyFiniScreen,
(FiniPluginObjectProc) wobblyFiniWindow
};
DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
}
static CompOption *
wobblyGetObjectOptions (CompPlugin *plugin,
CompObject *object,
int *count)
{
static GetPluginObjectOptionsProc dispTab[] = {
(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
(GetPluginObjectOptionsProc) wobblyGetDisplayOptions,
(GetPluginObjectOptionsProc) wobblyGetScreenOptions
};
*count = 0;
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
(void *) count, (plugin, object, count));
}
static CompBool
wobblySetObjectOption (CompPlugin *plugin,
CompObject *object,
const char *name,
CompOptionValue *value)
{
static SetPluginObjectOptionProc dispTab[] = {
(SetPluginObjectOptionProc) 0, /* SetCoreOption */
(SetPluginObjectOptionProc) wobblySetDisplayOption,
(SetPluginObjectOptionProc) wobblySetScreenOption
};
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
(plugin, object, name, value));
}
static Bool
wobblyInit (CompPlugin *p)
{
if (!compInitPluginMetadataFromInfo (&wobblyMetadata,
p->vTable->name,
wobblyDisplayOptionInfo,
WOBBLY_DISPLAY_OPTION_NUM,
wobblyScreenOptionInfo,
WOBBLY_SCREEN_OPTION_NUM))
return FALSE;
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
{
compFiniMetadata (&wobblyMetadata);
return FALSE;
}
compAddMetadataFromFile (&wobblyMetadata, p->vTable->name);
return TRUE;
}
static void
wobblyFini (CompPlugin *p)
{
freeDisplayPrivateIndex (displayPrivateIndex);
compFiniMetadata (&wobblyMetadata);
}
static CompMetadata *
wobblyGetMetadata (CompPlugin *plugin)
{
return &wobblyMetadata;
}
CompPluginVTable wobblyVTable = {
"wobbly",
wobblyGetMetadata,
wobblyInit,
wobblyFini,
wobblyInitObject,
wobblyFiniObject,
wobblyGetObjectOptions,
wobblySetObjectOption
};
CompPluginVTable *
getCompPluginInfo20070830 (void)
{
return &wobblyVTable;
}