/*
* Copyright © 2005 Novell, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Author: David Reveman <davidr@novell.com>
*/
/*
* Spring model implemented by Kristian Hogsberg.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <beryl.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 WOBBLY_EDGE_DISTANCE_DEFAULT 25.0f
#define WOBBLY_EDGE_DISTANCE_MIN 1.0f
#define WOBBLY_EDGE_DISTANCE_MAX 50.0f
#define WOBBLY_EDGE_DISTANCE_PRECISION 0.5f
#define WOBBLY_EDGE_VELOCITY_DEFAULT 13.0f
#define WOBBLY_EDGE_VELOCITY_MIN 1.0f
#define WOBBLY_EDGE_VELOCITY_MAX 50.0f
#define WOBBLY_EDGE_VELOCITY_PRECISION 0.5f
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;
Vector scale;
Point scaleOrigin;
Bool transformed;
Point topLeft;
Point bottomRight;
unsigned int edgeMask;
unsigned int snapCnt[4];
} Model;
#define WOBBLY_FRICTION_DEFAULT 4.0f
#define WOBBLY_FRICTION_MIN 0.1f
#define WOBBLY_FRICTION_MAX 10.0f
#define WOBBLY_FRICTION_PRECISION 0.1f
#define WOBBLY_SPRING_K_DEFAULT 10.0f
#define WOBBLY_SPRING_K_MIN 0.1f
#define WOBBLY_SPRING_K_MAX 10.0f
#define WOBBLY_SPRING_K_PRECISION 0.1f
#define WOBBLY_GRID_RESOLUTION_DEFAULT 48
#define WOBBLY_GRID_RESOLUTION_MIN 1
#define WOBBLY_GRID_RESOLUTION_MAX 64
#define WOBBLY_MIN_GRID_SIZE_DEFAULT 4
#define WOBBLY_MIN_GRID_SIZE_MIN 4
#define WOBBLY_MIN_GRID_SIZE_MAX 128
#define WOBBLY_MAXIMIZE_FRICTION_DEFAULT 4.0f
#define WOBBLY_MAXIMIZE_SPRING_K_DEFAULT 10.0f
#define WOBBLY_MAP_FRICTION_DEFAULT 4.0f
#define WOBBLY_MAP_SPRING_K_DEFAULT 10.0f
#define WOBBLY_URGENT_VELOCITY_DEFAULT 1.0f
#define WOBBLY_RELEASE_DEFAULT FALSE
#define WOBBLY_DEFAULT_SNAP_DEFAULT FALSE
#define WOBBLY_EDGE_ATTRACTION_DEFAULT FALSE
#define WOBBLY_URGENT_EFFECT_DEFAULT FALSE
#define WOBBLY_URGENT_IN_DEFAULT FALSE
#define WOBBLY_URGENT_UNIFORM_DEFAULT TRUE
typedef enum
{
WobblyEffectNone = 0,
WobblyEffectShiver
} WobblyEffect;
static char *effectName[] = {
N_("None"),
N_("Shiver")
};
static WobblyEffect effectType[] = {
WobblyEffectNone,
WobblyEffectShiver
};
#define NUM_EFFECT (sizeof (effectType) / sizeof (effectType[0]))
#define WOBBLY_MAP_DEFAULT (effectName[1])
#define WOBBLY_FOCUS_DEFAULT (effectName[0])
static char *mapWinType[] = {
N_("Splash"),
N_("Notification")
};
#define N_MAP_WIN_TYPE (sizeof (mapWinType) / sizeof (mapWinType[0]))
#define N_FOCUS_WIN_TYPE (0)
static char *moveWinType[] = {
N_("Unknown"),
N_("Splash"),
N_("Utility"),
N_("Dialog"),
N_("ModalDialog"),
N_("Normal")
};
#define nMods 4
static char *Mods[] = {
N_("Shift"),
N_("Alt"),
N_("Control"),
N_("Meta")
};
static int ModMask[] = {
ShiftMask,
CompAltMask,
ControlMask,
CompMetaMask,
};
#define N_MOVE_WIN_TYPE (sizeof (moveWinType) / sizeof (moveWinType[0]))
#define N_GRAB_WIN_TYPE (0)
#define WOBBLY_SNAP_MODIFIERS_DEFAULT ShiftMask
#define WOBBLY_MAXIMIZE_EFFECT_DEFAULT TRUE
#define WOBBLY_MOVE_EFFECT_DEFAULT TRUE
static int displayPrivateIndex;
#define WOBBLY_DISPLAY_OPTION_SNAP 0
#define WOBBLY_DISPLAY_OPTION_SHIVER 1
#define WOBBLY_DISPLAY_OPTION_DEFAULT_SNAP 2
#define WOBBLY_DISPLAY_OPTION_NUM 3
typedef struct _WobblyDisplay
{
int screenPrivateIndex;
Atom wmHintsAtom;
HandleEventProc handleEvent;
CompOption opt[WOBBLY_DISPLAY_OPTION_NUM];
int snapMask;
Bool snapping;
} WobblyDisplay;
#define WOBBLY_SCREEN_OPTION_MOVE_FRICTION 0
#define WOBBLY_SCREEN_OPTION_MOVE_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_TYPE 6
#define WOBBLY_SCREEN_OPTION_FOCUS_WINDOW_TYPE 7
#define WOBBLY_SCREEN_OPTION_GRAB_WINDOW_TYPE 8
#define WOBBLY_SCREEN_OPTION_MOVE_WINDOW_TYPE 9
#define WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT 10
#define WOBBLY_SCREEN_OPTION_MOVE_EFFECT 11
#define WOBBLY_SCREEN_OPTION_MAP_FRICTION 12
#define WOBBLY_SCREEN_OPTION_MAP_SPRING_K 13
#define WOBBLY_SCREEN_OPTION_GRAB_FRICTION 14
#define WOBBLY_SCREEN_OPTION_GRAB_SPRING_K 15
#define WOBBLY_SCREEN_OPTION_FOCUS_FRICTION 16
#define WOBBLY_SCREEN_OPTION_FOCUS_SPRING_K 17
#define WOBBLY_SCREEN_OPTION_MAXIMIZE_FRICTION 18
#define WOBBLY_SCREEN_OPTION_MAXIMIZE_SPRING_K 19
#define WOBBLY_SCREEN_OPTION_BELL_FRICTION 20
#define WOBBLY_SCREEN_OPTION_BELL_SPRING_K 21
#define WOBBLY_SCREEN_OPTION_RELEASE_FRICTION 22
#define WOBBLY_SCREEN_OPTION_RELEASE_SPRING_K 23
#define WOBBLY_SCREEN_OPTION_USE_RELEASE 24
#define WOBBLY_SCREEN_OPTION_URGENT_EFFECT 25
#define WOBBLY_SCREEN_OPTION_URGENT_FRICTION 26
#define WOBBLY_SCREEN_OPTION_URGENT_SPRING_K 27
#define WOBBLY_SCREEN_OPTION_URGENT_VELOCITY 28
#define WOBBLY_SCREEN_OPTION_URGENT_IN 29
#define WOBBLY_SCREEN_OPTION_URGENT_UNIFORM 30
#define WOBBLY_SCREEN_OPTION_EDGE_DISTANCE 31
#define WOBBLY_SCREEN_OPTION_EDGE_VELOCITY 32
#define WOBBLY_SCREEN_OPTION_EDGE_ATTRACTION 33
#define WOBBLY_SCREEN_OPTION_NUM 34
typedef struct _WobblyScreen
{
int windowPrivateIndex;
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
PaintScreenProc paintScreen;
PaintWindowProc paintWindow;
DamageWindowRectProc damageWindowRect;
AddWindowGeometryProc addWindowGeometry;
DrawWindowGeometryProc drawWindowGeometry;
DrawWindowTextureProc drawWindowTexture;
WindowResizeNotifyProc windowResizeNotify;
WindowMoveNotifyProc windowMoveNotify;
WindowGrabNotifyProc windowGrabNotify;
WindowUngrabNotifyProc windowUngrabNotify;
CompOption opt[WOBBLY_SCREEN_OPTION_NUM];
Bool wobblyWindows;
WobblyEffect mapEffect;
WobblyEffect focusEffect;
unsigned int mapWMask;
unsigned int focusWMask;
unsigned int moveWMask;
unsigned int grabWMask;
unsigned int grabMask;
CompWindow *grabWindow;
float edgeDistance;
float edgeVelocity;
} 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;
float friction;
float spring_k;
} WobblyWindow;
#define GET_WOBBLY_DISPLAY(d) \
((WobblyDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define WOBBLY_DISPLAY(d) \
WobblyDisplay *wd = GET_WOBBLY_DISPLAY (d)
#define GET_WOBBLY_SCREEN(s, wd) \
((WobblyScreen *) (s)->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)->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 Bool
wobblySetScreenOption(CompScreen * screen,
char *name, CompOptionValue * value)
{
CompOption *o;
int index;
WOBBLY_SCREEN(screen);
o = compFindOption(ws->opt, NUM_OPTIONS(ws), name, &index);
if (!o)
return FALSE;
switch (index)
{
/*
case WOBBLY_SCREEN_OPTION_FRICTION:
case WOBBLY_SCREEN_OPTION_SPRING_K:
*/
case WOBBLY_SCREEN_OPTION_MOVE_FRICTION:
case WOBBLY_SCREEN_OPTION_MOVE_SPRING_K:
case WOBBLY_SCREEN_OPTION_MAP_FRICTION:
case WOBBLY_SCREEN_OPTION_MAP_SPRING_K:
case WOBBLY_SCREEN_OPTION_GRAB_FRICTION:
case WOBBLY_SCREEN_OPTION_GRAB_SPRING_K:
case WOBBLY_SCREEN_OPTION_FOCUS_FRICTION:
case WOBBLY_SCREEN_OPTION_FOCUS_SPRING_K:
case WOBBLY_SCREEN_OPTION_MAXIMIZE_FRICTION:
case WOBBLY_SCREEN_OPTION_MAXIMIZE_SPRING_K:
case WOBBLY_SCREEN_OPTION_BELL_FRICTION:
case WOBBLY_SCREEN_OPTION_BELL_SPRING_K:
case WOBBLY_SCREEN_OPTION_RELEASE_FRICTION:
case WOBBLY_SCREEN_OPTION_RELEASE_SPRING_K:
case WOBBLY_SCREEN_OPTION_URGENT_FRICTION:
case WOBBLY_SCREEN_OPTION_URGENT_SPRING_K:
case WOBBLY_SCREEN_OPTION_URGENT_VELOCITY:
if (compSetFloatOption(o, value))
return TRUE;
break;
case WOBBLY_SCREEN_OPTION_GRID_RESOLUTION:
if (compSetIntOption(o, value))
return TRUE;
break;
case WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE:
if (compSetIntOption(o, value))
return TRUE;
break;
case WOBBLY_SCREEN_OPTION_MAP_EFFECT:
if (compSetStringOption(o, value))
{
int i;
for (i = 0; i < NUM_EFFECT; i++)
{
if (strcmp(o->value.s, effectName[i]) == 0)
{
ws->mapEffect = effectType[i];
return TRUE;
}
}
}
break;
case WOBBLY_SCREEN_OPTION_FOCUS_EFFECT:
if (compSetStringOption(o, value))
{
int i;
for (i = 0; i < NUM_EFFECT; i++)
{
if (strcmp(o->value.s, effectName[i]) == 0)
{
ws->focusEffect = effectType[i];
return TRUE;
}
}
}
break;
case WOBBLY_SCREEN_OPTION_MAP_WINDOW_TYPE:
if (compSetOptionList(o, value))
{
ws->mapWMask = compWindowTypeMaskFromStringList(&o->value);
return TRUE;
}
break;
case WOBBLY_SCREEN_OPTION_FOCUS_WINDOW_TYPE:
if (compSetOptionList(o, value))
{
ws->focusWMask = compWindowTypeMaskFromStringList(&o->value);
return TRUE;
}
break;
case WOBBLY_SCREEN_OPTION_MOVE_WINDOW_TYPE:
if (compSetOptionList(o, value))
{
ws->moveWMask = compWindowTypeMaskFromStringList(&o->value);
return TRUE;
}
break;
case WOBBLY_SCREEN_OPTION_GRAB_WINDOW_TYPE:
if (compSetOptionList(o, value))
{
ws->grabWMask = compWindowTypeMaskFromStringList(&o->value);
return TRUE;
}
break;
case WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT:
case WOBBLY_SCREEN_OPTION_MOVE_EFFECT:
case WOBBLY_SCREEN_OPTION_USE_RELEASE:
case WOBBLY_SCREEN_OPTION_URGENT_EFFECT:
case WOBBLY_SCREEN_OPTION_URGENT_IN:
case WOBBLY_SCREEN_OPTION_URGENT_UNIFORM:
case WOBBLY_SCREEN_OPTION_EDGE_ATTRACTION:
if (compSetBoolOption(o, value))
return TRUE;
break;
case WOBBLY_SCREEN_OPTION_EDGE_DISTANCE:
if (compSetFloatOption(o, value))
{
ws->edgeDistance = o->value.f;
return TRUE;
}
break;
case WOBBLY_SCREEN_OPTION_EDGE_VELOCITY:
if (compSetFloatOption(o, value))
{
ws->edgeVelocity = o->value.f;
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
static void wobblyScreenInitOptions(WobblyScreen * ws)
{
CompOption *o;
int i;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_FRICTION];
o->advanced = False;
o->name = "maximize_friction";
o->group = N_("Advanced");
o->subGroup = N_("Maximize");
o->displayHints = "";
o->shortDesc = N_("Maximize Friction");
o->longDesc = N_("Spring Friction for Maximize effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_MAXIMIZE_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_SPRING_K];
o->advanced = False;
o->name = "maximize_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Maximize");
o->displayHints = "";
o->shortDesc = N_("Maximize Spring K");
o->longDesc = N_("Spring Konstant for Maximize effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_MAXIMIZE_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MOVE_FRICTION];
o->advanced = False;
o->name = "move_friction";
o->group = N_("Advanced");
o->subGroup = N_("Move");
o->displayHints = "";
o->shortDesc = N_("Move Friction");
o->longDesc = N_("Spring Friction for Move types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MOVE_SPRING_K];
o->advanced = False;
o->name = "move_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Move");
o->displayHints = "";
o->shortDesc = N_("Move Spring K");
o->longDesc = N_("Spring Konstant for Move types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MAP_FRICTION];
o->advanced = False;
o->name = "map_friction";
o->group = N_("Advanced");
o->subGroup = N_("Map");
o->displayHints = "";
o->shortDesc = N_("Map Friction");
o->longDesc = N_("Spring Friction for Map types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_MAP_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MAP_SPRING_K];
o->advanced = False;
o->name = "map_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Map");
o->displayHints = "";
o->shortDesc = N_("Map Spring K");
o->longDesc = N_("Spring Konstant for Map types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_MAP_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_GRAB_FRICTION];
o->advanced = False;
o->name = "grab_friction";
o->group = N_("Advanced");
o->subGroup = N_("Grab");
o->displayHints = "";
o->shortDesc = N_("Grab Friction");
o->longDesc = N_("Spring Friction for Grab types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_GRAB_SPRING_K];
o->advanced = False;
o->name = "grab_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Grab");
o->displayHints = "";
o->shortDesc = N_("Grab Spring K");
o->longDesc = N_("Spring Konstant for Grab types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_FRICTION];
o->advanced = False;
o->name = "focus_friction";
o->group = N_("Advanced");
o->subGroup = N_("Focus");
o->displayHints = "";
o->shortDesc = N_("Focus Friction");
o->longDesc = N_("Spring Friction for Focus types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_SPRING_K];
o->advanced = False;
o->name = "focus_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Focus");
o->displayHints = "";
o->shortDesc = N_("Focus Spring K");
o->longDesc = N_("Spring Konstant for Focus types.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_GRID_RESOLUTION];
o->advanced = False;
o->name = "grid_resolution";
o->group = N_("Advanced");
o->subGroup = N_("Grid");
o->displayHints = "";
o->shortDesc = N_("Grid Resolution");
o->longDesc = N_("Vertex Grid Resolution.");
o->type = CompOptionTypeInt;
o->value.i = WOBBLY_GRID_RESOLUTION_DEFAULT;
o->rest.i.min = WOBBLY_GRID_RESOLUTION_MIN;
o->rest.i.max = WOBBLY_GRID_RESOLUTION_MAX;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE];
o->advanced = False;
o->name = "min_grid_size";
o->group = N_("Advanced");
o->subGroup = N_("Grid");
o->displayHints = "";
o->shortDesc = N_("Minimum Grid Size");
o->longDesc = N_("Minimum Vertex Grid Size.");
o->type = CompOptionTypeInt;
o->value.i = WOBBLY_MIN_GRID_SIZE_DEFAULT;
o->rest.i.min = WOBBLY_MIN_GRID_SIZE_MIN;
o->rest.i.max = WOBBLY_MIN_GRID_SIZE_MAX;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MAP_EFFECT];
o->advanced = False;
o->name = "map_effect";
o->group = N_("Advanced");
o->subGroup = N_("Map");
o->displayHints = "";
o->shortDesc = N_("Map Effect");
o->longDesc = N_("Map Window Effect.");
o->type = CompOptionTypeString;
o->value.s = strdup(WOBBLY_MAP_DEFAULT);
o->rest.s.string = effectName;
o->rest.s.nString = NUM_EFFECT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_EFFECT];
o->advanced = False;
o->name = "focus_effect";
o->group = N_("Advanced");
o->subGroup = N_("Focus");
o->displayHints = "";
o->shortDesc = N_("Focus Effect");
o->longDesc = N_("Focus Window Effect.");
o->type = CompOptionTypeString;
o->value.s = strdup(WOBBLY_FOCUS_DEFAULT);
o->rest.s.string = effectName;
o->rest.s.nString = NUM_EFFECT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MAP_WINDOW_TYPE];
o->advanced = False;
o->name = "map_window_types";
o->group = N_("Advanced");
o->subGroup = N_("Map");
o->displayHints = "";
o->shortDesc = N_("Map Window Types");
o->longDesc = N_("Window Types that should Wobble when Mapped.");
o->type = CompOptionTypeList;
o->value.list.type = CompOptionTypeString;
o->value.list.nValue = N_MAP_WIN_TYPE;
o->value.list.value = malloc(sizeof(CompOptionValue) * N_MAP_WIN_TYPE);
for (i = 0; i < N_MAP_WIN_TYPE; i++)
o->value.list.value[i].s = strdup(mapWinType[i]);
o->rest.s.string = (char **)windowTypeString;
o->rest.s.nString = nWindowTypeString;
ws->mapWMask = compWindowTypeMaskFromStringList(&o->value);
o = &ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_WINDOW_TYPE];
o->advanced = False;
o->name = "focus_window_types";
o->group = N_("Advanced");
o->subGroup = N_("Focus");
o->displayHints = "";
o->shortDesc = N_("Focus Window Types");
o->longDesc = N_("Window Types that should Wobble when Focused.");
o->type = CompOptionTypeList;
o->value.list.type = CompOptionTypeString;
o->value.list.nValue = N_FOCUS_WIN_TYPE;
o->value.list.value = NULL;
o->rest.s.string = (char **)windowTypeString;
o->rest.s.nString = nWindowTypeString;
ws->focusWMask = compWindowTypeMaskFromStringList(&o->value);
o = &ws->opt[WOBBLY_SCREEN_OPTION_MOVE_WINDOW_TYPE];
o->advanced = False;
o->name = "move_window_types";
o->group = N_("Advanced");
o->subGroup = N_("Move");
o->displayHints = "";
o->shortDesc = N_("Move Window Types");
o->longDesc = N_("Window Types that should Wobble when Moved.");
o->type = CompOptionTypeList;
o->value.list.type = CompOptionTypeString;
o->value.list.nValue = N_MOVE_WIN_TYPE;
o->value.list.value = malloc(sizeof(CompOptionValue) * N_MOVE_WIN_TYPE);
for (i = 0; i < N_MOVE_WIN_TYPE; i++)
o->value.list.value[i].s = strdup(moveWinType[i]);
o->rest.s.string = (char **)windowTypeString;
o->rest.s.nString = nWindowTypeString;
ws->moveWMask = compWindowTypeMaskFromStringList(&o->value);
o = &ws->opt[WOBBLY_SCREEN_OPTION_GRAB_WINDOW_TYPE];
o->advanced = False;
o->name = "grab_window_types";
o->group = N_("Advanced");
o->subGroup = N_("Grab");
o->displayHints = "";
o->shortDesc = N_("Grab Window Types");
o->longDesc = N_("Window Types that should Wobble when Grabbed.");
o->type = CompOptionTypeList;
o->value.list.type = CompOptionTypeString;
o->value.list.nValue = N_GRAB_WIN_TYPE;
o->value.list.value = NULL;
o->rest.s.string = (char **)windowTypeString;
o->rest.s.nString = nWindowTypeString;
ws->grabWMask = compWindowTypeMaskFromStringList(&o->value);
o = &ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT];
o->advanced = False;
o->name = "maximize_effect";
o->group = N_("Advanced");
o->subGroup = N_("Maximize");
o->displayHints = "";
o->shortDesc = N_("Maximize Effect");
o->longDesc =
N_("Wobble Effect when Maximizing and unMaximizing windows.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_MAXIMIZE_EFFECT_DEFAULT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_MOVE_EFFECT];
o->advanced = False;
o->name = "move_effect";
o->group = N_("Advanced");
o->subGroup = N_("Move");
o->displayHints = "";
o->shortDesc = N_("Move Effect");
o->longDesc = N_("Wobble windows when you Move them.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_MOVE_EFFECT_DEFAULT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_BELL_FRICTION];
o->advanced = False;
o->name = "visual_bell_friction";
o->group = N_("Advanced");
o->subGroup = N_("Visual bell");
o->displayHints = "";
o->shortDesc = N_("Visual Bell Friction");
o->longDesc = N_("Spring Friction for Bell effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_BELL_SPRING_K];
o->advanced = False;
o->name = "visual_bell_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Visual bell");
o->displayHints = "";
o->shortDesc = N_("Visual Bell Spring K");
o->longDesc = N_("Spring Konstant for Bell effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_USE_RELEASE];
o->advanced = False;
o->name = "release_effect";
o->group = N_("Advanced");
o->subGroup = N_("Release");
o->displayHints = "";
o->shortDesc = N_("Release Effect");
o->longDesc = N_("Use settings for Releasing windows.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_RELEASE_DEFAULT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_RELEASE_FRICTION];
o->advanced = False;
o->name = "release_friction";
o->group = N_("Advanced");
o->subGroup = N_("Release");
o->displayHints = "";
o->shortDesc = N_("Release Friction");
o->longDesc = N_("Spring Friction for Release effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_RELEASE_SPRING_K];
o->advanced = False;
o->name = "release_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Release");
o->displayHints = "";
o->shortDesc = N_("Release Spring K");
o->longDesc = N_("Spring Konstant for Release effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_URGENT_EFFECT];
o->advanced = False;
o->name = "urgent_effect";
o->group = N_("Advanced");
o->subGroup = N_("Urgent");
o->displayHints = "";
o->shortDesc = N_("Use Urgent Effect");
o->longDesc = N_("Make Urgent windows Wobble.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_URGENT_EFFECT_DEFAULT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_URGENT_FRICTION];
o->advanced = False;
o->name = "urgent_friction";
o->group = N_("Advanced");
o->subGroup = N_("Urgent");
o->displayHints = "";
o->shortDesc = N_("Urgent Friction");
o->longDesc = N_("Spring Friction for Urgent effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_FRICTION_DEFAULT;
o->rest.f.min = WOBBLY_FRICTION_MIN;
o->rest.f.max = WOBBLY_FRICTION_MAX;
o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_URGENT_SPRING_K];
o->advanced = False;
o->name = "urgent_spring_k";
o->group = N_("Advanced");
o->subGroup = N_("Urgent");
o->displayHints = "";
o->shortDesc = N_("Urgent Spring K");
o->longDesc = N_("Spring Konstant for Urgent effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_SPRING_K_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_URGENT_VELOCITY];
o->advanced = False;
o->name = "urgent_velocity";
o->group = N_("Advanced");
o->subGroup = N_("Urgent");
o->displayHints = "";
o->shortDesc = N_("Urgent Velocity");
o->longDesc = N_("Window velocity for Urgent effect.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_URGENT_VELOCITY_DEFAULT;
o->rest.f.min = WOBBLY_SPRING_K_MIN;
o->rest.f.max = WOBBLY_SPRING_K_MAX;
o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_URGENT_IN];
o->advanced = False;
o->name = "urgent_effect_in";
o->group = N_("Advanced");
o->subGroup = N_("Urgent");
o->displayHints = "";
o->shortDesc = N_("Urgent Effect In");
o->longDesc = N_("Make Urgent windows Wobble Inward.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_URGENT_IN_DEFAULT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_URGENT_UNIFORM];
o->advanced = False;
o->name = "urgent_effect_uniform";
o->group = N_("Advanced");
o->subGroup = N_("Urgent");
o->displayHints = "";
o->shortDesc = N_("Urgent Effect Uniform");
o->longDesc = N_("Make Urgent windows Wobble Uniformly.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_URGENT_UNIFORM_DEFAULT;
o = &ws->opt[WOBBLY_SCREEN_OPTION_EDGE_DISTANCE];
o->advanced = False;
o->name = "edge_distance";
o->group = N_("Advanced");
o->subGroup = N_("Edge");
o->displayHints = "";
o->shortDesc = N_("Edge Snapping Distance");
o->longDesc = N_("Edge Snapping Distance.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_EDGE_DISTANCE_DEFAULT;
o->rest.f.min = WOBBLY_EDGE_DISTANCE_MIN;
o->rest.f.max = WOBBLY_EDGE_DISTANCE_MAX;
o->rest.f.precision = WOBBLY_EDGE_DISTANCE_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_EDGE_VELOCITY];
o->advanced = False;
o->name = "edge_velocity";
o->group = N_("Advanced");
o->subGroup = N_("Edge");
o->displayHints = "";
o->shortDesc = N_("Edge Snapping Velocity");
o->longDesc = N_("Edge Snapping Velocity.");
o->type = CompOptionTypeFloat;
o->value.f = WOBBLY_EDGE_VELOCITY_DEFAULT;
o->rest.f.min = WOBBLY_EDGE_VELOCITY_MIN;
o->rest.f.max = WOBBLY_EDGE_VELOCITY_MAX;
o->rest.f.precision = WOBBLY_EDGE_VELOCITY_PRECISION;
o = &ws->opt[WOBBLY_SCREEN_OPTION_EDGE_ATTRACTION];
o->advanced = False;
o->name = "edge_attraction";
o->group = N_("Basic");
o->subGroup = N_("Snap");
o->displayHints = "";
o->shortDesc = N_("Edge Attraction");
o->longDesc = N_("Enable Edge Attraction.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_EDGE_ATTRACTION_DEFAULT;
}
static CompOption *wobblyGetScreenOptions(CompScreen * screen, int *count)
{
if (screen)
{
WOBBLY_SCREEN(screen);
*count = NUM_OPTIONS(ws);
return ws->opt;
}
else
{
WobblyScreen *ws = malloc(sizeof(WobblyScreen));
wobblyScreenInitOptions(ws);
*count = NUM_OPTIONS(ws);
return ws->opt;
}
}
#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;
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);
if (x >= w->screen->outputDev[output].region.extents.x1)
{
CompWindow *p;
v1 = w->screen->outputDev[output].region.extents.x1;
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->output.top - w->output.top;
e = p->attrib.y + p->height + p->output.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 = w->screen->outputDev[output].region.extents.x2;
}
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;
WOBBLY_SCREEN(w->screen);
if (ws->opt[WOBBLY_SCREEN_OPTION_EDGE_ATTRACTION].value.b)
object->vertEdge.attract = v1 + ws->edgeDistance;
else object->vertEdge.attract = v1;
object->vertEdge.velocity = ws->edgeVelocity;
}
static void findNextEastEdge(CompWindow * w, Object * object)
{
int v, v1, v2;
int s, start;
int e, end;
int x;
int output;
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);
if (x <= w->screen->outputDev[output].region.extents.x2)
{
CompWindow *p;
v1 = w->screen->outputDev[output].region.extents.x2;
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->output.top - w->output.top;
e = p->attrib.y + p->height + p->output.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 = w->screen->outputDev[output].region.extents.x2;
}
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;
WOBBLY_SCREEN(w->screen);
if (ws->opt[WOBBLY_SCREEN_OPTION_EDGE_ATTRACTION].value.b)
object->vertEdge.attract = v1 - ws->edgeDistance;
else object->vertEdge.attract = v1;
object->vertEdge.velocity = ws->edgeVelocity;
}
static void findNextNorthEdge(CompWindow * w, Object * object)
{
int v, v1, v2;
int s, start;
int e, end;
int y;
int output;
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);
if (y >= w->screen->outputDev[output].region.extents.y1)
{
CompWindow *p;
v1 = w->screen->outputDev[output].region.extents.y1;
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->output.left - w->output.left;
e = p->attrib.x + p->width + p->output.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 = w->screen->outputDev[output].region.extents.y1;
}
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;
WOBBLY_SCREEN(w->screen);
if (ws->opt[WOBBLY_SCREEN_OPTION_EDGE_ATTRACTION].value.b)
object->horzEdge.attract = v1 + ws->edgeDistance;
else object->horzEdge.attract = v1;
object->horzEdge.velocity = ws->edgeVelocity;
}
static void findNextSouthEdge(CompWindow * w, Object * object)
{
int v, v1, v2;
int s, start;
int e, end;
int y;
int output;
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);
if (y <= w->screen->outputDev[output].region.extents.y2)
{
CompWindow *p;
v1 = w->screen->outputDev[output].region.extents.y2;
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->output.left - w->output.left;
e = p->attrib.x + p->width + p->output.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 = w->screen->outputDev[output].region.extents.y2;
}
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;
WOBBLY_SCREEN(w->screen);
if (ws->opt[WOBBLY_SCREEN_OPTION_EDGE_ATTRACTION].value.b)
object->horzEdge.attract = v1 - ws->edgeDistance;
else object->horzEdge.attract = v1;
object->horzEdge.velocity = ws->edgeVelocity;
}
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
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 scale;
float w, h;
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
objectReleaseEdge(CompWindow * w, Model * model, Object * object,
short int edge)
{
if (((NORTH == edge || edge == SOUTH)
&& (fabs(object->velocity.y) > object->horzEdge.velocity))
|| ((WEST == edge || edge == EAST)
&& (fabs(object->velocity.x) > object->vertEdge.velocity)))
{
model->snapCnt[edge]--;
if (edge == WEST || edge == EAST)
{
object->position.x += object->velocity.x * 2.0f;
object->vertEdge.snapped = FALSE;
}
else
{
object->position.y += object->velocity.y * 2.0f;
object->horzEdge.snapped = FALSE;
}
object->edgeMask = 0;
modelUpdateSnapping(w, model);
return TRUE;
}
if (edge == WEST || edge == EAST)
object->velocity.x = 0.0f;
else
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 == FALSE ||
objectReleaseEdge(window, model, object, WEST))
{
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 == FALSE ||
objectReleaseEdge(window, model, object, EAST))
{
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 == FALSE ||
objectReleaseEdge(window, model, object, NORTH))
{
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 == FALSE ||
objectReleaseEdge(window, model, object, SOUTH))
{
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 offscreen windows */
if (w->state & CompWindowStateOffscreenMask)
return FALSE;
/* 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;
ws->wobblyWindows = 0;
for (w = s->windows; w; w = w->next)
{
ww = GET_WOBBLY_WINDOW(w, ws);
friction = ww->friction;
springK = ww->spring_k;
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)
{
/* 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;
}
}
else
{
ww->model = NULL;
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))
damageScreen(s);
UNWRAP(ws, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP(ws, s, donePaintScreen, wobblyDonePaintScreen);
}
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;
if (w->vCount == 0)
{
// reset
w->indexCount = 0;
w->texCoordSize = 2;
}
vSize = 2 + w->texUnits * w->texCoordSize;
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;
nVertices++;
if (x == x2)
break;
}
if (y == y2)
break;
}
pClip++;
}
w->vCount = nVertices;
w->indexCount = nIndices;
}
else
{
UNWRAP(ws, w->screen, addWindowGeometry);
(*w->screen->addWindowGeometry) (w, matrix, nMatrix, region, clip);
WRAP(ws, w->screen, addWindowGeometry, wobblyAddWindowGeometry);
}
}
static void wobblyDrawWindowGeometry(CompWindow * w)
{
WOBBLY_WINDOW(w);
if (ww->wobbly)
{
int texUnit = w->texUnits;
int currentTexUnit = 0;
int stride = 2 + texUnit * w->texCoordSize;
GLfloat *vertices = w->vertices + (stride - 2);
stride *= sizeof(GLfloat);
glVertexPointer(2, GL_FLOAT, stride, vertices);
while (texUnit--)
{
if (texUnit != currentTexUnit)
{
w->screen->clientActiveTexture(GL_TEXTURE0_ARB + texUnit);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
currentTexUnit = texUnit;
}
vertices -= 2;
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);
}
}
else
{
WOBBLY_SCREEN(w->screen);
UNWRAP(ws, w->screen, drawWindowGeometry);
(*w->screen->drawWindowGeometry) (w);
WRAP(ws, w->screen, drawWindowGeometry, wobblyDrawWindowGeometry);
}
}
static Bool
wobblyPaintWindow(CompWindow * w,
const WindowPaintAttrib * attrib,
Region region, unsigned int mask)
{
Bool status;
WOBBLY_SCREEN(w->screen);
WOBBLY_WINDOW(w);
if (ww->wobbly)
{
if (mask & PAINT_WINDOW_SOLID_MASK)
return FALSE;
mask |= PAINT_WINDOW_TRANSFORMED_MASK;
}
UNWRAP(ws, w->screen, paintWindow);
status = (*w->screen->paintWindow) (w, attrib, 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;
}
}
}
}
wd->snapping = FALSE;
return FALSE;
}
static Bool
wobblyEnableKeySnapping(CompDisplay * d,
CompAction * action,
CompActionState state,
CompOption * option, int nOption)
{
WOBBLY_DISPLAY(d);
if (!wd->opt[WOBBLY_DISPLAY_OPTION_DEFAULT_SNAP].value.b)
return wobblyEnableSnapping(d, NULL, 0, NULL, 0);
else
return wobblyDisableSnapping(d, NULL, 0, NULL, 0);
}
static Bool
wobblyDisableKeySnapping(CompDisplay * d,
CompAction * action,
CompActionState state,
CompOption * option, int nOption)
{
WOBBLY_DISPLAY(d);
if (wd->opt[WOBBLY_DISPLAY_OPTION_DEFAULT_SNAP].value.b)
return wobblyEnableSnapping(d, NULL, 0, NULL, 0);
else
return wobblyDisableSnapping(d, NULL, 0, NULL, 0);
}
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;
}
return FALSE;
}
static void wobblyHandleEvent(CompDisplay * d, XEvent * event)
{
Window activeWindow = 0;
CompWindow *w;
CompScreen *s;
WOBBLY_DISPLAY(d);
switch (event->type)
{
case ClientMessage:
if (event->xclient.message_type ==
XInternAtom(d->display, "_BERYL_WOBBLY_SHIVER", 0))
{
CompOption o[1];
o[0].type = CompOptionTypeInt;
o[0].name = "window";
o[0].value.i = event->xclient.window;
wobblyShiver(d, NULL, 0, o, 1);
}
break;
case PropertyNotify:
if (event->xproperty.atom == d->winActiveAtom)
activeWindow = d->activeWindow;
break;
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;
unsigned int mods = 0xffffffff;
if (wd->snapMask)
mods = wd->snapMask;
if ((stateEvent->mods & mods) == mods)
wobblyEnableKeySnapping(d, NULL, 0, NULL, 0);
else
wobblyDisableKeySnapping(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->moveWMask & ws->grabWindow->type) &&
ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
{
WOBBLY_WINDOW(ws->grabWindow);
if (ww->state & MAXIMIZE_STATE)
{
WOBBLY_WINDOW(ws->grabWindow);
if (ww->model && ww->grabbed)
{
int dx, dy;
if (ww->state & CompWindowStateMaximizedHorzMask)
dx = d->pointerX - d->lastPointerX;
else
dx = 0;
if (ww->state & CompWindowStateMaximizedVertMask)
dy = d->pointerY - d->lastPointerY;
else
dy = 0;
ww->model->anchorObject->position.x += dx;
ww->model->anchorObject->position.y += dy;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
}
}
}
}
break;
case PropertyNotify:
if (event->xproperty.atom == d->winActiveAtom)
{
if (d->activeWindow != activeWindow)
{
w = findWindowAtDisplay(d, d->activeWindow);
if (w && isWobblyWin(w))
{
WOBBLY_WINDOW(w);
WOBBLY_SCREEN(w->screen);
if ((ws->focusWMask & w->type) &&
ws->focusEffect && wobblyEnsureModel(w))
{
switch (ws->focusEffect)
{
case WobblyEffectShiver:
modelAdjustObjectsForShiver
(ww->model,
WIN_X(w), WIN_Y(w), WIN_W(w), WIN_H(w));
default:
break;
}
ww->friction =
ws->
opt[WOBBLY_SCREEN_OPTION_FOCUS_FRICTION].
value.f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_SPRING_K].
value.f;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
}
}
}
}
else if (event->xproperty.atom == wd->wmHintsAtom)
{
w = findWindowAtDisplay(d, event->xproperty.window);
if (w)
{
XWMHints *xwmh =
XGetWMHints(w->screen->display->display, w->id);
if (xwmh)
{
WOBBLY_WINDOW(w);
WOBBLY_SCREEN(w->screen);
if (isWobblyWin(w)
&& (xwmh->flags & XUrgencyHint)
&& wobblyEnsureModel(w)
&& ws->
opt[WOBBLY_SCREEN_OPTION_URGENT_EFFECT].value.b)
{
Spring *s;
int i;
float vf;
for (i = 0; i < ww->model->numSprings; i++)
{
s = &ww->model->springs[i];
vf = ws->
opt
[WOBBLY_SCREEN_OPTION_URGENT_VELOCITY].
value.f / 30;
if (ws->
opt[WOBBLY_SCREEN_OPTION_URGENT_IN].value.b)
{
s->b->velocity.x -= s->offset.x * vf;
s->b->velocity.y -= s->offset.y * vf;
s->a->velocity.x += s->offset.x * vf;
s->a->velocity.y += s->offset.y * vf;
}
else
{
s->b->velocity.x += s->offset.x * vf;
s->b->velocity.y += s->offset.y * vf;
s->a->velocity.x -= s->offset.x * vf;
s->a->velocity.y -= s->offset.y * vf;
}
}
if (!ws->
opt[WOBBLY_SCREEN_OPTION_URGENT_UNIFORM].value.b)
modelSetMiddleAnchor
(ww->model,
WIN_X(w), WIN_Y(w), WIN_W(w), WIN_H(w));
else if (ww->model->anchorObject)
ww->model->anchorObject->immobile = FALSE;
modelAdjustObjectsForShiver
(ww->model, WIN_X(w),
WIN_Y(w), WIN_W(w), WIN_H(w));
ww->friction =
ws->
opt[WOBBLY_SCREEN_OPTION_URGENT_FRICTION].
value.f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_URGENT_SPRING_K].
value.f;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
}
else
{
ww->friction =
ws->
opt[WOBBLY_SCREEN_OPTION_MOVE_FRICTION].value.
f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_MOVE_SPRING_K].
value.f;
}
XFree(xwmh);
}
}
}
default:
break;
}
}
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 = ®ion.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, ®ion);
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))
{
WOBBLY_WINDOW(w);
WOBBLY_SCREEN(w->screen);
if (ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b)
wobblyEnsureModel(w);
if ((ws->mapWMask & w->type) &&
ws->mapEffect && wobblyEnsureModel(w))
{
switch (ws->mapEffect)
{
case WobblyEffectShiver:
modelAdjustObjectsForShiver(ww->
model,
WIN_X
(w),
WIN_Y(w), WIN_W(w), WIN_H(w));
default:
break;
}
ww->friction =
ws->opt[WOBBLY_SCREEN_OPTION_MAP_FRICTION].value.f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_MAP_SPRING_K].value.f;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
}
}
}
return status;
}
static void wobblyWindowResizeNotify(CompWindow * w, int dx, int dy,
int dwidth, int dheight, Bool preview)
{
WOBBLY_SCREEN(w->screen);
WOBBLY_WINDOW(w);
if (!preview && ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_EFFECT].value.b &&
isWobblyWin(w) && ((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->friction =
ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_FRICTION].value.f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_MAXIMIZE_SPRING_K].value.f;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
damagePendingOnScreen(w->screen);
}
}
else if (!preview && ww->model)
{
if (!ww->wobbly)
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 (!preview && ww->model && ww->grabbed)
{
if (ww->model->anchorObject)
ww->model->anchorObject->immobile = FALSE;
ww->model->anchorObject = modelFindNearestObject(ww->model,
w->screen->display->
pointerX,
w->screen->display->
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, preview);
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->friction =
ws->opt[WOBBLY_SCREEN_OPTION_MOVE_FRICTION].value.f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_MOVE_SPRING_K].value.f;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
}
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)
{
WOBBLY_SCREEN(w->screen);
WOBBLY_DISPLAY(w->screen->display);
if (!ws->grabWindow)
{
ws->grabMask = mask;
ws->grabWindow = w;
}
if (ws->opt[WOBBLY_SCREEN_OPTION_MOVE_EFFECT].value.b)
{
if ((mask & CompWindowGrabButtonMask) &&
(ws->moveWMask & w->type) && isWobblyWin(w))
{
WOBBLY_WINDOW(w);
if (wobblyEnsureModel(w))
{
Spring *s;
int i;
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;
if (mask & CompWindowGrabMoveMask)
{
modelDisableSnapping(w, ww->model);
if (wd->snapping)
modelUpdateSnapping(w, ww->model);
}
if (ws->grabWMask & w->type)
{
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->friction =
ws->opt[WOBBLY_SCREEN_OPTION_GRAB_FRICTION].value.
f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_GRAB_SPRING_K].value.
f;
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
}
}
}
}
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 (ws->grabWindow == w)
{
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));
}
if (ws->opt[WOBBLY_SCREEN_OPTION_USE_RELEASE].value.b)
{
ww->friction =
ws->opt[WOBBLY_SCREEN_OPTION_RELEASE_FRICTION].value.
f;
ww->spring_k =
ws->opt[WOBBLY_SCREEN_OPTION_RELEASE_SPRING_K].value.
f;
}
ww->wobbly |= WobblyInitial;
ws->wobblyWindows |= ww->wobbly;
}
ww->grabbed = FALSE;
}
UNWRAP(ws, w->screen, windowUngrabNotify);
(*w->screen->windowUngrabNotify) (w);
WRAP(ws, w->screen, windowUngrabNotify, wobblyWindowUngrabNotify);
}
static Bool
wobblyPaintScreen(CompScreen * s,
const ScreenPaintAttrib * sAttrib,
Region region, int output, unsigned int mask)
{
Bool status;
WOBBLY_SCREEN(s);
if (ws->wobblyWindows)
mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
UNWRAP(ws, s, paintScreen);
status = (*s->paintScreen) (s, sAttrib, region, output, mask);
WRAP(ws, s, paintScreen, wobblyPaintScreen);
return status;
}
static Bool
wobblySetDisplayOption(CompDisplay * display,
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_DEFAULT_SNAP:
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:
if (compSetOptionList(o, value))
{
int i;
wd->snapMask = 0;
for (i = 0; i < o->value.list.nValue; i++)
{
int j;
for (j = 0; j < nMods; j++)
if (strcmp(o->value.list.value[i].s, Mods[j]) == 0)
wd->snapMask |= ModMask[j];
}
return TRUE;
}
break;
case WOBBLY_DISPLAY_OPTION_SHIVER:
if (setDisplayAction(display, o, value))
return TRUE;
break;
default:
break;
}
return FALSE;
}
static void wobblyDisplayInitOptions(WobblyDisplay * wd)
{
CompOption *o;
CompOptionValue *v;
o = &wd->opt[WOBBLY_DISPLAY_OPTION_SNAP];
o->advanced = False;
o->name = "snap";
o->group = N_("Basic");
o->subGroup = N_("Snap");
o->displayHints = "";
o->shortDesc = N_("Snap Windows Modifier");
o->longDesc = N_("Press all of the selected keys while "
"moving Windows to cause them to Snap");
o->type = CompOptionTypeList;
v = malloc(sizeof(CompOptionValue));
o->value.list.type = CompOptionTypeString;
o->value.list.nValue = 1;
o->value.list.value = v;
v->s = strdup(N_("Shift"));
o->rest.s.nString = nMods;
o->rest.s.string = Mods;
o = &wd->opt[WOBBLY_DISPLAY_OPTION_DEFAULT_SNAP];
o->advanced = False;
o->name = "default_snap";
o->group = N_("Basic");
o->subGroup = N_("Snap");
o->displayHints = "";
o->shortDesc = N_("Default Snapping to On");
o->longDesc = N_("Reverse meaning of Snapping toggle.");
o->type = CompOptionTypeBool;
o->value.b = WOBBLY_DEFAULT_SNAP_DEFAULT;
o = &wd->opt[WOBBLY_DISPLAY_OPTION_SHIVER];
o->advanced = False;
o->name = "shiver";
o->group = N_("Basic");
o->subGroup = N_("Shiver");
o->displayHints = "";
o->shortDesc = N_("Shiver");
o->longDesc = N_("Make window Shiver.");
o->type = CompOptionTypeAction;
o->value.action.initiate = wobblyShiver;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.state = CompActionStateInitBell;
o->value.action.state |= CompActionStateInitKey;
o->value.action.state |= CompActionStateInitButton;
o->value.action.type = 0;
}
static CompOption *wobblyGetDisplayOptions(CompDisplay * display, int *count)
{
if (display)
{
WOBBLY_DISPLAY(display);
*count = NUM_OPTIONS(wd);
return wd->opt;
}
else
{
WobblyDisplay *wd = malloc(sizeof(WobblyDisplay));
wobblyDisplayInitOptions(wd);
*count = NUM_OPTIONS(wd);
return wd->opt;
}
}
static Bool wobblyInitDisplay(CompPlugin * p, CompDisplay * d)
{
WobblyDisplay *wd;
wd = malloc(sizeof(WobblyDisplay));
if (!wd)
return FALSE;
wd->screenPrivateIndex = allocateScreenPrivateIndex(d);
if (wd->screenPrivateIndex < 0)
{
free(wd);
return FALSE;
}
wd->wmHintsAtom = XInternAtom(d->display, "WM_HINTS", FALSE);
WRAP(wd, d, handleEvent, wobblyHandleEvent);
wd->snapping = FALSE;
wd->snapMask = ShiftMask;
wobblyDisplayInitOptions(wd);
d->privates[displayPrivateIndex].ptr = wd;
return TRUE;
}
static void wobblyFiniDisplay(CompPlugin * p, CompDisplay * d)
{
WOBBLY_DISPLAY(d);
freeScreenPrivateIndex(d, wd->screenPrivateIndex);
UNWRAP(wd, d, handleEvent);
free(wd);
}
static Bool wobblyInitScreen(CompPlugin * p, CompScreen * s)
{
WobblyScreen *ws;
WOBBLY_DISPLAY(s->display);
int i;
ws = malloc(sizeof(WobblyScreen));
if (!ws)
return FALSE;
ws->windowPrivateIndex = allocateWindowPrivateIndex(s);
if (ws->windowPrivateIndex < 0)
{
free(ws);
return FALSE;
}
ws->wobblyWindows = FALSE;
ws->mapEffect = WobblyEffectShiver;
ws->focusEffect = WobblyEffectNone;
ws->grabMask = 0;
ws->grabWindow = NULL;
ws->edgeDistance = WOBBLY_EDGE_DISTANCE_DEFAULT;
ws->edgeVelocity = WOBBLY_EDGE_VELOCITY_DEFAULT;
wobblyScreenInitOptions(ws);
/* init the focus effect */
for (i = 0; i < NUM_EFFECT; i++)
{
if (strcmp
(ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_EFFECT].value.s,
effectName[i]) == 0)
{
ws->focusEffect = effectType[i];
break;
}
}
WRAP(ws, s, preparePaintScreen, wobblyPreparePaintScreen);
WRAP(ws, s, donePaintScreen, wobblyDonePaintScreen);
WRAP(ws, s, paintScreen, wobblyPaintScreen);
WRAP(ws, s, paintWindow, wobblyPaintWindow);
WRAP(ws, s, damageWindowRect, wobblyDamageWindowRect);
WRAP(ws, s, addWindowGeometry, wobblyAddWindowGeometry);
WRAP(ws, s, drawWindowGeometry, wobblyDrawWindowGeometry);
WRAP(ws, s, windowResizeNotify, wobblyWindowResizeNotify);
WRAP(ws, s, windowMoveNotify, wobblyWindowMoveNotify);
WRAP(ws, s, windowGrabNotify, wobblyWindowGrabNotify);
WRAP(ws, s, windowUngrabNotify, wobblyWindowUngrabNotify);
s->privates[wd->screenPrivateIndex].ptr = ws;
return TRUE;
}
static void wobblyFiniScreen(CompPlugin * p, CompScreen * s)
{
WOBBLY_SCREEN(s);
freeWindowPrivateIndex(s, ws->windowPrivateIndex);
free(ws->opt[WOBBLY_SCREEN_OPTION_MAP_EFFECT].value.s);
free(ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_EFFECT].value.s);
UNWRAP(ws, s, preparePaintScreen);
UNWRAP(ws, s, donePaintScreen);
UNWRAP(ws, s, paintScreen);
UNWRAP(ws, s, paintWindow);
UNWRAP(ws, s, damageWindowRect);
UNWRAP(ws, s, addWindowGeometry);
UNWRAP(ws, s, drawWindowGeometry);
UNWRAP(ws, s, windowResizeNotify);
UNWRAP(ws, s, windowMoveNotify);
UNWRAP(ws, s, windowGrabNotify);
UNWRAP(ws, s, windowUngrabNotify);
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;
ww->friction = 3.0;
ww->spring_k = 8.0;
w->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);
if (ww->model)
{
free(ww->model->objects);
free(ww->model);
}
free(ww);
}
static Bool wobblyInit(CompPlugin * p)
{
displayPrivateIndex = allocateDisplayPrivateIndex();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void wobblyFini(CompPlugin * p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex(displayPrivateIndex);
}
CompPluginDep wobblyDeps[] = {
{CompPluginRuleAfter, "decoration"}
,
};
CompPluginFeature wobblyFeatures[] = {
{"edgeresistance"}
};
CompPluginVTable wobblyVTable = {
"wobbly",
N_("Wobbly Windows"),
N_("Use spring model for wobbly window effect"),
wobblyInit,
wobblyFini,
wobblyInitDisplay,
wobblyFiniDisplay,
wobblyInitScreen,
wobblyFiniScreen,
wobblyInitWindow,
wobblyFiniWindow,
wobblyGetDisplayOptions,
wobblySetDisplayOption,
wobblyGetScreenOptions,
wobblySetScreenOption,
wobblyDeps,
sizeof(wobblyDeps) / sizeof(wobblyDeps[0]),
wobblyFeatures,
sizeof(wobblyFeatures) / sizeof(wobblyFeatures[0]),
BERYL_ABI_INFO,
"beryl-plugins",
"effects",
0,
0,
True,
};
CompPluginVTable *getCompPluginInfo(void)
{
return &wobblyVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1