/* * 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 */ /* * Spring model implemented by Kristian Hogsberg. */ #include #include #include #include #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; }