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