summaryrefslogtreecommitdiff
path: root/snap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'snap.cpp')
-rw-r--r--snap.cpp1181
1 files changed, 1181 insertions, 0 deletions
diff --git a/snap.cpp b/snap.cpp
new file mode 100644
index 0000000..da316bc
--- /dev/null
+++ b/snap.cpp
@@ -0,0 +1,1181 @@
+/*
+ * Beryl Snap Plugin
+ * Author : Guillaume "iXce" Seguin
+ * Email : ixce@beryl-project.org
+ *
+ * Ported by : Patrick "marex" Niklaus
+ * Email : marex@beryl-project.org
+ *
+ * Copyright (C) 2007 Guillaume Seguin
+ *
+ * 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.
+ */
+
+/*
+ * TODO
+ * - Apply Edge Resistance to resize
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <compiz-core.h>
+
+#include "snap_options.h"
+
+/*
+ * The neat window coordinates macros
+ */
+#define WIN_X(w) ((w)->attrib.x - (w)->input.left)
+#define WIN_Y(w) ((w)->attrib.y - (w)->input.top)
+#define WIN_W(w) ((w)->width + (w)->input.left + (w)->input.right)
+#define WIN_H(w) ((w)->height + (w)->input.top + (w)->input.bottom)
+
+/*
+ * The window we should snap too if snapping to windows
+ */
+#define SNAP_WINDOW_TYPE (CompWindowTypeNormalMask | \
+ CompWindowTypeToolbarMask | \
+ CompWindowTypeMenuMask | \
+ CompWindowTypeUtilMask)
+
+#define VerticalSnap (1L << 0)
+#define HorizontalSnap (1L << 1)
+
+#define MoveGrab (1L << 0)
+#define ResizeGrab (1L << 1)
+
+typedef enum
+{
+ LeftEdge = 0,
+ RightEdge,
+ TopEdge,
+ BottomEdge
+} EdgeType;
+
+typedef struct _Edge Edge;
+
+/* Custom Edge struct, linked list
+ * Position, start, end meanings are specific to type :
+ * - LeftEdge/RightEdge : position : x, start/end : y1/y2
+ * - TopEdge/BottomEdge : position : y, start/end : x1/x2
+ * id/passed are used during visibility detection when adding edges
+ * snapped is straight forward
+ */
+struct _Edge
+{
+ Edge *prev;
+ Edge *next;
+
+ int position;
+ int start;
+ int end;
+ EdgeType type;
+ Bool screenEdge;
+
+ Window id;
+ Bool passed;
+
+ Bool snapped;
+};
+
+static int displayPrivateIndex;
+
+typedef struct _SnapDisplay
+{
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ int avoidSnapMask;
+
+ // Used to check is avoidSnapMask is currently matched
+ Bool snapping;
+} SnapDisplay;
+
+#define SNAP_SCREEN_OPTION_SNAP_TYPE 0
+#define SNAP_SCREEN_OPTION_EDGES_CATEGORIES 1
+#define SNAP_SCREEN_OPTION_RESISTANCE_DISTANCE 2
+#define SNAP_SCREEN_OPTION_ATTRACTION_DISTANCE 3
+#define SNAP_SCREEN_OPTION_NUM 4
+
+typedef struct _SnapScreen
+{
+ int windowPrivateIndex;
+
+ WindowResizeNotifyProc windowResizeNotify;
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowGrabNotifyProc windowGrabNotify;
+ WindowUngrabNotifyProc windowUngrabNotify;
+} SnapScreen;
+
+typedef struct _SnapWindow
+{
+ // Linked lists
+ Edge *edges;
+ Edge *reverseEdges;
+
+ // bitfield
+ int snapDirection;
+
+ // dx/dy/dw/dh when a window is resisting to user
+ int dx;
+ int dy;
+ int dw;
+ int dh;
+
+ // Internals
+ Bool snapped;
+ int grabbed;
+
+ // Internal, avoids infinite notify loops
+ Bool skipNotify;
+} SnapWindow;
+
+#define GET_SNAP_DISPLAY(d) \
+ ((SnapDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define SNAP_DISPLAY(d) \
+ SnapDisplay *sd = GET_SNAP_DISPLAY (d)
+
+#define GET_SNAP_SCREEN(s, sd) \
+ ((SnapScreen *) (s)->base.privates[(sd)->screenPrivateIndex].ptr)
+
+#define SNAP_SCREEN(s) \
+ SnapScreen *ss = GET_SNAP_SCREEN (s, GET_SNAP_DISPLAY (s->display))
+
+#define GET_SNAP_WINDOW(w, ss) \
+ ((SnapWindow *) (w)->base.privates[(ss)->windowPrivateIndex].ptr)
+
+#define SNAP_WINDOW(w) \
+ SnapWindow *sw = GET_SNAP_WINDOW (w, \
+ GET_SNAP_SCREEN (w->screen, \
+ GET_SNAP_DISPLAY (w->screen->display)))
+
+// Helper functions ------------------------------------------------------------
+
+/*
+ * copied from Beryl's core
+ */
+static void snapScreenGetOutputDevRect(CompScreen * s, int outputDev, XRectangle * outputRect)
+{
+ if (!outputRect)
+ return;
+
+ if (outputDev >= s->nOutputDev)
+ outputDev = 0;
+
+ outputRect->x = s->outputDev[outputDev].region.extents.x1;
+ outputRect->y = s->outputDev[outputDev].region.extents.y1;
+ outputRect->width =
+ s->outputDev[outputDev].region.extents.x2 -
+ s->outputDev[outputDev].region.extents.x1;
+ outputRect->height =
+ s->outputDev[outputDev].region.extents.y2 -
+ s->outputDev[outputDev].region.extents.y1;
+}
+
+/*
+ * Wrapper functions to avoid infinite notify loops
+ */
+static void snapMoveWindow(CompWindow * w, int dx, int dy)
+{
+ SNAP_WINDOW(w);
+ sw->skipNotify = TRUE;
+ moveWindow(w, dx, dy, TRUE, TRUE);
+ warpPointer(w->screen, dx, dy);
+ sw->skipNotify = FALSE;
+}
+
+static void snapResizeWindow(CompWindow * w, int dx, int dy, int dw, int dh)
+{
+ SNAP_WINDOW(w);
+ //fprintf (stderr, "Resizing: %d, %d, %d, %d\n", dx, dy, dw, dh);
+ sw->skipNotify = TRUE;
+ resizeWindow(w, w->attrib.x + dx, w->attrib.y + dy,
+ w->attrib.width + dw, w->attrib.height + dh,
+ w->attrib.border_width);
+ sw->skipNotify = FALSE;
+}
+
+static void snapFreeEdges(CompWindow * w)
+{
+ SNAP_WINDOW(w);
+ Edge *current = sw->edges, *next;
+
+ while (current)
+ {
+ next = current->next;
+ free(current);
+ current = next;
+ }
+ sw->edges = sw->reverseEdges = NULL;
+}
+
+static void snapRemoveEdge(Edge * edge)
+{
+ if (edge == NULL)
+ return;
+ if (edge->prev)
+ edge->prev->next = edge->next;
+ if (edge->next)
+ edge->next->prev = edge->prev;
+ free(edge);
+}
+
+static Edge *snapAddEdge(Edge ** edges, Edge ** reverseEdges, Window id,
+ int position, int start, int end, EdgeType type,
+ Bool screenEdge)
+{
+ Edge *edge = malloc(sizeof(Edge));
+
+ if (!edge)
+ return NULL;
+ edge->next = NULL;
+ edge->position = position;
+ edge->start = start;
+ edge->end = end;
+ edge->type = type;
+ edge->screenEdge = screenEdge;
+ edge->snapped = FALSE;
+ edge->passed = FALSE;
+ edge->id = id;
+ if (!*edges)
+ {
+ edge->prev = NULL;
+ *reverseEdges = *edges = edge;
+ }
+ else
+ {
+ edge->prev = *reverseEdges;
+ edge->prev->next = edge;
+ *reverseEdges = edge;
+ }
+ return edge;
+}
+
+/*
+ * Add an edge for each rectangle of the region
+ */
+static void snapAddRegionEdges(SnapWindow * sw, Edge * parent, Region region)
+{
+ Edge *edge;
+ int i, position, start, end;
+
+ for (i = 0; i < region->numRects; i++)
+ {
+ switch (parent->type)
+ {
+ case LeftEdge:
+ case RightEdge:
+ position = region->rects[i].x1;
+ start = region->rects[i].y1;
+ end = region->rects[i].y2;
+ break;
+ case TopEdge:
+ case BottomEdge:
+ default:
+ position = region->rects[i].y1;
+ start = region->rects[i].x1;
+ end = region->rects[i].x2;
+ }
+ edge = snapAddEdge(&sw->edges, &sw->reverseEdges, parent->id,
+ position, start, end, parent->type,
+ parent->screenEdge);
+ if (edge)
+ edge->passed = parent->passed;
+ }
+}
+
+/* Checks if a window is considered a snap window. If it's
+ * not visible, returns false. If it's a panel and we're
+ * snapping to screen edges, it's considered a snap-window.
+ */
+
+#define UNLIKELY(x) __builtin_expect(!!(x),0)
+
+static inline Bool isSnapWindow(CompWindow * w)
+{
+ //SNAP_SCREEN(w->screen);
+
+ if (UNLIKELY(!w))
+ return FALSE;
+ if (w->invisible || w->hidden || w->minimized)
+ return FALSE;
+ if ((w->type & SNAP_WINDOW_TYPE) &&
+ (snapGetEdgesCategoriesMask(w->screen) & EdgesCategoriesWindowEdgesMask))
+ return TRUE;
+ if (w->struts &&
+ (snapGetEdgesCategoriesMask(w->screen) & EdgesCategoriesScreenEdgesMask))
+ return TRUE;
+ return FALSE;
+}
+
+// Edges update functions ------------------------------------------------------
+/*
+ * Detect visible windows edges
+ */
+static void snapUpdateWindowsEdges(CompWindow * w)
+{
+ CompWindow *c = NULL;
+ Edge *e = NULL, *next = NULL;
+
+ SNAP_WINDOW(w);
+ Region edgeRegion, resultRegion;
+ XRectangle rect;
+ Bool remove = FALSE;
+
+ // First add all the windows
+ c = w->screen->windows;
+ while (c)
+ {
+ // Just check that we're not trying to snap to current window,
+ // that the window is not invisible and of a valid type
+ if (c == w || !isSnapWindow(c))
+ {
+ c = c->next;
+ continue;
+ }
+ snapAddEdge(&sw->edges, &sw->reverseEdges, c->id,
+ WIN_Y(c), WIN_X(c), WIN_X(c) + WIN_W(c), TopEdge, FALSE);
+ snapAddEdge(&sw->edges, &sw->reverseEdges, c->id,
+ WIN_Y(c) + WIN_H(c), WIN_X(c), WIN_X(c) + WIN_W(c),
+ BottomEdge, FALSE);
+ snapAddEdge(&sw->edges, &sw->reverseEdges, c->id,
+ WIN_X(c), WIN_Y(c), WIN_Y(c) + WIN_H(c), LeftEdge, FALSE);
+ snapAddEdge(&sw->edges, &sw->reverseEdges, c->id,
+ WIN_X(c) + WIN_W(c), WIN_Y(c), WIN_Y(c) + WIN_H(c),
+ RightEdge, FALSE);
+ c = c->next;
+ }
+
+ // Now strip invisible edges
+ // Loop through all the windows stack, and through all the edges
+ // If an edge has been passed, check if it's in the region window,
+ // if the edge is fully under the window, drop it, or if it's only
+ // partly covered, cut it/split it in one/two smaller visible edges
+ for (c = w->screen->windows; c; c = c->next)
+ {
+ if (c == w || !isSnapWindow(c))
+ continue;
+ for (e = sw->edges; e; e = next)
+ {
+ if (!e->passed)
+ {
+ if (e->id == c->id)
+ e->passed = TRUE;
+ next = e->next;
+ continue;
+ }
+ switch (e->type)
+ {
+ case LeftEdge:
+ case RightEdge:
+ rect.x = e->position;
+ rect.y = e->start;
+ rect.width = 1;
+ rect.height = e->end - e->start;
+ break;
+ case TopEdge:
+ case BottomEdge:
+ default:
+ rect.x = e->start;
+ rect.y = e->position;
+ rect.width = e->end - e->start;
+ rect.height = 1;
+ }
+ // If the edge is in the window region, remove it,
+ // if it's partly in the region, split it
+ edgeRegion = XCreateRegion();
+ resultRegion = XCreateRegion();
+ XUnionRectWithRegion(&rect, edgeRegion, edgeRegion);
+ XSubtractRegion(edgeRegion, c->region, resultRegion);
+ if (XEmptyRegion(resultRegion))
+ remove = TRUE;
+ else if (!XEqualRegion(edgeRegion, resultRegion))
+ {
+ snapAddRegionEdges(sw, e, resultRegion);
+ remove = TRUE;
+ }
+ next = e->next;
+ if (remove)
+ {
+ if (e->prev == NULL)
+ sw->edges = e->next;
+ if (e->next == NULL)
+ sw->reverseEdges = e->prev;
+ snapRemoveEdge(e);
+ remove = FALSE;
+ }
+ XDestroyRegion(resultRegion);
+ XDestroyRegion(edgeRegion);
+ }
+ }
+}
+
+/*
+ * Loop on outputDevs and add the extents as edges
+ * Note that left side is a right edge, right side a left edge,
+ * top side a bottom edge and bottom side a top edge,
+ * since they will be snapped as the right/left/bottom/top edge of a window
+ */
+static void snapUpdateScreenEdges(CompWindow * w)
+{
+ CompWindow *c = NULL;
+ Edge *e = NULL, *next = NULL;
+
+ SNAP_WINDOW(w);
+ Region edgeRegion, resultRegion;
+ XRectangle rect;
+ Bool remove = FALSE;
+
+ XRectangle area;
+ int i;
+
+ for (i = 0; i < w->screen->nOutputDev; i++)
+ {
+ snapScreenGetOutputDevRect(w->screen, i, &area);
+ snapAddEdge(&sw->edges, &sw->reverseEdges, 0,
+ area.y, area.x, area.x + area.width - 1, BottomEdge, TRUE);
+ snapAddEdge(&sw->edges, &sw->reverseEdges, 0,
+ area.y + area.height, area.x,
+ area.x + area.width - 1, TopEdge, TRUE);
+ snapAddEdge(&sw->edges, &sw->reverseEdges, 0,
+ area.x, area.y, area.y + area.height - 1, RightEdge, TRUE);
+ snapAddEdge(&sw->edges, &sw->reverseEdges, 0,
+ area.x + area.width, area.y,
+ area.y + area.height - 1, LeftEdge, TRUE);
+ }
+
+ // Drop screen edges parts that are under struts, basically apply the
+ // same strategy than for windows edges visibility
+ for (c = w->screen->windows; c; c = c->next)
+ {
+ if (c == w || !c->struts)
+ continue;
+ for (e = sw->edges; e; e = next)
+ {
+ if (!e->screenEdge)
+ {
+ next = e->next;
+ continue;
+ }
+ switch (e->type)
+ {
+ case LeftEdge:
+ case RightEdge:
+ rect.x = e->position;
+ rect.y = e->start;
+ rect.width = 1;
+ rect.height = e->end - e->start;
+ break;
+ case TopEdge:
+ case BottomEdge:
+ default:
+ rect.x = e->start;
+ rect.y = e->position;
+ rect.width = e->end - e->start;
+ rect.height = 1;
+ }
+ edgeRegion = XCreateRegion();
+ resultRegion = XCreateRegion();
+ XUnionRectWithRegion(&rect, edgeRegion, edgeRegion);
+ XSubtractRegion(edgeRegion, c->region, resultRegion);
+ if (XEmptyRegion(resultRegion))
+ remove = TRUE;
+ else if (!XEqualRegion(edgeRegion, resultRegion))
+ {
+ snapAddRegionEdges(sw, e, resultRegion);
+ remove = TRUE;
+ }
+ next = e->next;
+ if (remove)
+ {
+ if (e->prev == NULL)
+ sw->edges = e->next;
+ if (e->next == NULL)
+ sw->reverseEdges = e->prev;
+ snapRemoveEdge(e);
+ remove = FALSE;
+ }
+ XDestroyRegion(resultRegion);
+ XDestroyRegion(edgeRegion);
+ }
+ }
+}
+
+/*
+ * Clean edges and fill it again with appropriate edges
+ */
+static void snapUpdateEdges(CompWindow * w)
+{
+ //SNAP_SCREEN(w->screen);
+
+ snapFreeEdges(w);
+
+ snapUpdateWindowsEdges(w);
+
+ if (snapGetEdgesCategoriesMask(w->screen) & EdgesCategoriesScreenEdgesMask)
+ snapUpdateScreenEdges(w);
+}
+
+// Edges checking functions (move) ---------------------------------------------
+
+/*
+ * Find nearest edge in the direction set by "type",
+ * w is the grabbed window, position/start/end are the window edges coordinates
+ * before : if true the window has to be before the edge (top/left being origin)
+ * snapDirection : just an helper, related to type
+ */
+static void
+snapMoveCheckNearestEdge(CompWindow * w, int position, int start, int end,
+ Bool before, EdgeType type, int snapDirection)
+{
+ //SNAP_SCREEN(w->screen);
+ SNAP_WINDOW(w);
+ Edge *current = sw->edges;
+ Edge *edge = current;
+ int dist, min = 65535;
+
+ while (current)
+ {
+ // Skip wrong type or outbound edges
+ if (current->type != type
+ || current->end < start || current->start > end)
+ {
+ current = current->next;
+ continue;
+ }
+ // Compute distance
+ dist = before ? position - current->position
+ : current->position - position;
+ // Update minimum distance if needed
+ if (dist < min && dist >= 0)
+ {
+ min = dist;
+ edge = current;
+ }
+ // 0-dist edge, just break
+ if (dist == 0)
+ break;
+ // Unsnap edges that aren't snapped anymore
+ if (current->snapped && dist > snapGetResistanceDistance(w->screen))
+ current->snapped = FALSE;
+ current = current->next;
+ }
+ // We found a 0-dist edge, or we have a snapping candidate
+ if (min == 0 || (min <= snapGetAttractionDistance(w->screen)
+ && snapGetSnapTypeMask(w->screen) & SnapTypeEdgeAttractionMask))
+ {
+ // Update snapping data
+ if (snapGetSnapTypeMask(w->screen) & SnapTypeEdgeResistanceMask)
+ {
+ sw->snapped = TRUE;
+ sw->snapDirection |= snapDirection;
+ }
+ // Attract the window if needed, moving it of the correct dist
+ if (min != 0 && !edge->snapped)
+ {
+ edge->snapped = TRUE;
+ switch (type)
+ {
+ case LeftEdge:
+ snapMoveWindow(w, min, 0);
+ break;
+ case RightEdge:
+ snapMoveWindow(w, -min, 0);
+ break;
+ case TopEdge:
+ snapMoveWindow(w, 0, min);
+ break;
+ case BottomEdge:
+ snapMoveWindow(w, 0, -min);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Call the previous function for each of the 4 sides of the window
+ */
+static void snapMoveCheckEdges(CompWindow * w)
+{
+ snapMoveCheckNearestEdge(w, WIN_X(w),
+ WIN_Y(w), WIN_Y(w) + WIN_H(w),
+ TRUE, RightEdge, HorizontalSnap);
+ snapMoveCheckNearestEdge(w, WIN_X(w) + WIN_W(w),
+ WIN_Y(w), WIN_Y(w) + WIN_H(w),
+ FALSE, LeftEdge, HorizontalSnap);
+ snapMoveCheckNearestEdge(w, WIN_Y(w),
+ WIN_X(w), WIN_X(w) + WIN_W(w),
+ TRUE, BottomEdge, VerticalSnap);
+ snapMoveCheckNearestEdge(w, WIN_Y(w) + WIN_H(w),
+ WIN_X(w), WIN_X(w) + WIN_W(w),
+ FALSE, TopEdge, VerticalSnap);
+}
+
+// Edges checking functions (resize) -------------------------------------------
+
+/*
+ * Similar function for Snap on Resize
+ */
+static void
+snapResizeCheckNearestEdge(CompWindow * w, int position, int start, int end,
+ Bool before, EdgeType type, int snapDirection)
+{
+ //SNAP_SCREEN(w->screen);
+ SNAP_WINDOW(w);
+ Edge *current = sw->edges;
+ Edge *edge = current;
+ int dist, min = 65535;
+
+ while (current)
+ {
+ // Skip wrong type or outbound edges
+ if (current->type != type
+ || current->end < start || current->start > end)
+ {
+ current = current->next;
+ continue;
+ }
+ // Compute distance
+ dist = before ? position - current->position
+ : current->position - position;
+ // Update minimum distance if needed
+ if (dist < min && dist >= 0)
+ {
+ min = dist;
+ edge = current;
+ }
+ // 0-dist edge, just break
+ if (dist == 0)
+ break;
+ // Unsnap edges that aren't snapped anymore
+ if (current->snapped && dist > snapGetResistanceDistance(w->screen))
+ current->snapped = FALSE;
+ current = current->next;
+ }
+ // We found a 0-dist edge, or we have a snapping candidate
+ if (min == 0 || (min <= snapGetAttractionDistance(w->screen)
+ && snapGetSnapTypeMask(w->screen) & SnapTypeEdgeAttractionMask))
+ {
+ // Update snapping data
+ if (snapGetSnapTypeMask(w->screen) & SnapTypeEdgeResistanceMask)
+ {
+ sw->snapped = TRUE;
+ sw->snapDirection |= snapDirection;
+ }
+ // FIXME : this needs resize-specific code.
+ // Attract the window if needed, moving it of the correct dist
+ if (min != 0 && !edge->snapped)
+ {
+ edge->snapped = TRUE;
+ switch (type)
+ {
+ case LeftEdge:
+ snapResizeWindow(w, min, 0, 0, 0);
+ break;
+ case RightEdge:
+ snapResizeWindow(w, -min, 0, 0, 0);
+ break;
+ case TopEdge:
+ snapResizeWindow(w, 0, min, 0, 0);
+ break;
+ case BottomEdge:
+ snapResizeWindow(w, 0, -min, 0, 0);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Call the previous function for each of the 4 sides of the window
+ */
+static void snapResizeCheckEdges(CompWindow * w, int dx, int dy, int dw, int dh)
+{
+ int x, y, width, height;
+ x = WIN_W(w);
+ y = WIN_Y(w);
+ width = WIN_W(w);
+ height = WIN_H(w);
+
+ snapResizeCheckNearestEdge(w, x, y, y + height, TRUE, RightEdge,
+ HorizontalSnap);
+ snapResizeCheckNearestEdge(w, x + width, y, y + height, FALSE, LeftEdge,
+ HorizontalSnap);
+ snapResizeCheckNearestEdge(w, y, x, x + width, TRUE, BottomEdge,
+ VerticalSnap);
+ snapResizeCheckNearestEdge(w, y + height, x, x + width, FALSE, TopEdge,
+ VerticalSnap);
+}
+
+// avoidSnap functions ---------------------------------------------------------
+
+static Bool
+snapEnableSnapping(CompDisplay * d,
+ CompAction * action,
+ CompActionState state, CompOption * option, int nOption)
+{
+ SNAP_DISPLAY(d);
+ sd->snapping = TRUE;
+ return FALSE;
+}
+
+static Bool
+snapDisableSnapping(CompDisplay * d,
+ CompAction * action,
+ CompActionState state, CompOption * option, int nOption)
+{
+ SNAP_DISPLAY(d);
+ sd->snapping = FALSE;
+ return FALSE;
+}
+
+// Check if avoidSnap is matched, and enable/disable snap consequently
+static void snapHandleEvent(CompDisplay * d, XEvent * event)
+{
+ SNAP_DISPLAY(d);
+
+ if (event->type == d->xkbEvent)
+ {
+ XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
+
+ if (xkbEvent->xkb_type == XkbStateNotify)
+ {
+ XkbStateNotifyEvent *stateEvent = (XkbStateNotifyEvent *) event;
+ unsigned int mods = 0xffffffff;
+
+ if (sd->avoidSnapMask)
+ mods = sd->avoidSnapMask;
+
+ if ((stateEvent->mods & mods) == mods)
+ snapDisableSnapping(d, NULL, 0, NULL, 0);
+ else
+ snapEnableSnapping(d, NULL, 0, NULL, 0);
+ }
+ }
+
+ UNWRAP(sd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP(sd, d, handleEvent, snapHandleEvent);
+}
+
+// Events notifications --------------------------------------------------------
+
+static void
+snapWindowResizeNotify(CompWindow * w, int dx, int dy, int dw, int dh)
+{
+ SNAP_DISPLAY (w->screen->display);
+ SNAP_SCREEN(w->screen);
+ SNAP_WINDOW (w);
+
+ UNWRAP(ss, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dw, dh);
+ WRAP(ss, w->screen, windowResizeNotify, snapWindowResizeNotify);
+
+ // avoid-infinite-notify-loop mode/not grabbed
+ if (sw->skipNotify || !(sw->grabbed & ResizeGrab))
+ return;
+
+ // we have to avoid snapping but there's still some buffered moving
+ if (!sd->snapping && (sw->dx || sw->dy || sw->dw || sw->dh))
+ {
+ snapResizeWindow(w, sw->dx, sw->dy, sw->dw, sw->dh);
+ sw->dx = sw->dy = sw->dw = sw->dh = 0;
+ return;
+ }
+
+ // avoiding snap, nothing buffered
+ if (!sd->snapping)
+ return;
+
+ // apply edge resistance
+ if (snapGetSnapTypeMask(w->screen) & SnapTypeEdgeResistanceMask)
+ {
+ // If there's horizontal snapping, add dx to current buffered
+ // dx and resist (move by -dx) or release the window and move
+ // by buffered dx - dx, same for dh
+ if (sw->snapped && sw->snapDirection & HorizontalSnap)
+ {
+ sw->dx += dx;
+ if (sw->dx < snapGetResistanceDistance(w->screen)
+ && sw->dx > -snapGetResistanceDistance(w->screen))
+ snapResizeWindow(w, -dx, 0, 0, 0);
+ else
+ {
+ snapResizeWindow(w, sw->dx - dx, 0, 0, 0);
+ sw->dx = 0;
+ if (!sw->dw)
+ sw->snapDirection &= VerticalSnap;
+ }
+ sw->dw += dw;
+ if (sw->dw < snapGetResistanceDistance(w->screen)
+ && sw->dw > -snapGetResistanceDistance(w->screen))
+ snapResizeWindow(w, 0, 0, -dw, 0);
+ else
+ {
+ snapResizeWindow(w, 0, 0, sw->dw - dw, 0);
+ sw->dw = 0;
+ if (!sw->dx)
+ sw->snapDirection &= VerticalSnap;
+ }
+ }
+ // Same for vertical snapping and dy/dh
+ if (sw->snapped && sw->snapDirection & VerticalSnap)
+ {
+ sw->dy += dy;
+ if (sw->dy < snapGetResistanceDistance(w->screen)
+ && sw->dy > -snapGetResistanceDistance(w->screen))
+ snapResizeWindow(w, 0, -dy, 0, 0);
+ else
+ {
+ snapResizeWindow(w, 0, sw->dy - dy, 0, 0);
+ sw->dy = 0;
+ if (!sw->dh)
+ sw->snapDirection &= HorizontalSnap;
+ }
+ sw->dh += dh;
+ if (sw->dh < snapGetResistanceDistance(w->screen)
+ && sw->dh > -snapGetResistanceDistance(w->screen))
+ snapResizeWindow(w, 0, 0, 0, -dh);
+ else
+ {
+ snapResizeWindow(w, 0, 0, 0, sw->dh - dh);
+ sw->dh = 0;
+ if (!sw->dy)
+ sw->snapDirection &= HorizontalSnap;
+ }
+ }
+ // If we are no longer snapping in any direction, reset snapped
+ if (sw->snapped && !sw->snapDirection)
+ sw->snapped = FALSE;
+ }
+
+ // If we don't already snap vertically and horizontally,
+ // check edges status
+ if (sw->snapDirection != (VerticalSnap | HorizontalSnap))
+ snapResizeCheckEdges(w, dx, dy, dw, dh);
+}
+
+static void
+snapWindowMoveNotify(CompWindow * w, int dx, int dy, Bool immediate)
+{
+ SNAP_DISPLAY(w->screen->display);
+ SNAP_SCREEN(w->screen);
+ SNAP_WINDOW(w);
+
+ UNWRAP(ss, w->screen, windowMoveNotify);
+ (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
+ WRAP(ss, w->screen, windowMoveNotify, snapWindowMoveNotify);
+
+ // avoid-infinite-notify-loop mode/not grabbed
+ if (sw->skipNotify || !(sw->grabbed & MoveGrab))
+ return;
+
+ // we have to avoid snapping but there's still some buffered moving
+ if (!sd->snapping && (sw->dx || sw->dy))
+ {
+ snapMoveWindow(w, sw->dx, sw->dy);
+ sw->dx = sw->dy = 0;
+ return;
+ }
+
+ // avoiding snap, nothing buffered
+ if (!sd->snapping)
+ return;
+
+ // apply edge resistance
+ if (snapGetSnapTypeMask(w->screen) & SnapTypeEdgeResistanceMask)
+ {
+ // If there's horizontal snapping, add dx to current buffered
+ // dx and resist (move by -dx) or release the window and move
+ // by buffered dx - dx
+ if (sw->snapped && sw->snapDirection & HorizontalSnap)
+ {
+ sw->dx += dx;
+ if (sw->dx < snapGetResistanceDistance(w->screen)
+ && sw->dx > -snapGetResistanceDistance(w->screen))
+ snapMoveWindow(w, -dx, 0);
+ else
+ {
+ snapMoveWindow(w, sw->dx - dx, 0);
+ sw->dx = 0;
+ sw->snapDirection &= VerticalSnap;
+ }
+ }
+ // Same for vertical snapping and dy
+ if (sw->snapped && sw->snapDirection & VerticalSnap)
+ {
+ sw->dy += dy;
+ if (sw->dy < snapGetResistanceDistance(w->screen)
+ && sw->dy > -snapGetResistanceDistance(w->screen))
+ snapMoveWindow(w, 0, -dy);
+ else
+ {
+ snapMoveWindow(w, 0, sw->dy - dy);
+ sw->dy = 0;
+ sw->snapDirection &= HorizontalSnap;
+ }
+ }
+ // If we are no longer snapping in any direction, reset snapped
+ if (sw->snapped && !sw->snapDirection)
+ sw->snapped = FALSE;
+ }
+ // If we don't already snap vertically and horizontally,
+ // check edges status
+ if (sw->snapDirection != (VerticalSnap | HorizontalSnap))
+ snapMoveCheckEdges(w);
+}
+
+/*
+ * Initiate snap, get edges
+ */
+static void
+snapWindowGrabNotify(CompWindow * w,
+ int x, int y, unsigned int state, unsigned int mask)
+{
+ SNAP_SCREEN(w->screen);
+ SNAP_WINDOW(w);
+
+ sw->grabbed = (mask & CompWindowGrabResizeMask) ? ResizeGrab : MoveGrab;
+ snapUpdateEdges(w);
+
+ UNWRAP(ss, w->screen, windowGrabNotify);
+ (*w->screen->windowGrabNotify) (w, x, y, state, mask);
+ WRAP(ss, w->screen, windowGrabNotify, snapWindowGrabNotify);
+}
+
+/*
+ * Clean edges data, reset dx/dy to avoid buggy moves
+ * when snap will be triggered again.
+ */
+static void snapWindowUngrabNotify(CompWindow * w)
+{
+ SNAP_SCREEN(w->screen);
+ SNAP_WINDOW(w);
+
+ snapFreeEdges(w);
+ sw->snapped = FALSE;
+ sw->snapDirection = 0;
+ sw->grabbed = 0;
+ sw->dx = sw->dy = sw->dw = sw->dh = 0;
+
+ UNWRAP(ss, w->screen, windowUngrabNotify);
+ (*w->screen->windowUngrabNotify) (w);
+ WRAP(ss, w->screen, windowUngrabNotify, snapWindowUngrabNotify);
+}
+
+// Internal stuff --------------------------------------------------------------
+
+static void snapDisplayOptionChanged(CompDisplay *d, CompOption *opt, SnapDisplayOptions num)
+{
+ SNAP_DISPLAY(d);
+
+ switch (num)
+ {
+ case SnapDisplayOptionAvoidSnap:
+ {
+ unsigned int mask = snapGetAvoidSnapMask(d);
+ sd->avoidSnapMask = 0;
+ if (mask & AvoidSnapShiftMask)
+ sd->avoidSnapMask |= ShiftMask;
+ if (mask & AvoidSnapAltMask)
+ sd->avoidSnapMask |= CompAltMask;
+ if (mask & AvoidSnapControlMask)
+ sd->avoidSnapMask |= ControlMask;
+ if (mask & AvoidSnapMetaMask)
+ sd->avoidSnapMask |= CompMetaMask;
+ }
+
+ default:
+ break;
+ }
+}
+
+static Bool snapInitDisplay(CompPlugin * p, CompDisplay * d)
+{
+ SnapDisplay *sd;
+
+ if (!checkPluginABI ("core", CORE_ABIVERSION))
+ return FALSE;
+
+ sd = malloc(sizeof(SnapDisplay));
+ if (!sd)
+ return FALSE;
+
+ sd->screenPrivateIndex = allocateScreenPrivateIndex(d);
+ if (sd->screenPrivateIndex < 0)
+ {
+ free(sd);
+ return FALSE;
+ }
+
+ WRAP(sd, d, handleEvent, snapHandleEvent);
+
+ snapSetAvoidSnapNotify(d, snapDisplayOptionChanged);
+
+ sd->avoidSnapMask = 0;
+ sd->snapping = TRUE;
+
+ d->base.privates[displayPrivateIndex].ptr = sd;
+
+ return TRUE;
+}
+
+static void snapFiniDisplay(CompPlugin * p, CompDisplay * d)
+{
+ SNAP_DISPLAY(d);
+
+ freeScreenPrivateIndex(d, sd->screenPrivateIndex);
+
+ UNWRAP(sd, d, handleEvent);
+
+ free(sd);
+}
+
+static Bool snapInitScreen(CompPlugin * p, CompScreen * s)
+{
+ SnapScreen *ss;
+
+ SNAP_DISPLAY(s->display);
+
+ ss = malloc(sizeof(SnapScreen));
+ if (!ss)
+ return FALSE;
+
+ ss->windowPrivateIndex = allocateWindowPrivateIndex(s);
+ if (ss->windowPrivateIndex < 0)
+ {
+ free(ss);
+ return FALSE;
+ }
+
+ //WRAP(ss, s, windowResizeNotify, snapWindowResizeNotify);
+ WRAP(ss, s, windowMoveNotify, snapWindowMoveNotify);
+ WRAP(ss, s, windowGrabNotify, snapWindowGrabNotify);
+ WRAP(ss, s, windowUngrabNotify, snapWindowUngrabNotify);
+
+ s->base.privates[sd->screenPrivateIndex].ptr = ss;
+
+ return TRUE;
+}
+
+static void snapFiniScreen(CompPlugin * p, CompScreen * s)
+{
+ SNAP_SCREEN(s);
+
+ freeWindowPrivateIndex(s, ss->windowPrivateIndex);
+
+ //UNWRAP(ss, s, windowResizeNotify);
+ UNWRAP(ss, s, windowMoveNotify);
+ UNWRAP(ss, s, windowGrabNotify);
+ UNWRAP(ss, s, windowUngrabNotify);
+
+ free(ss);
+}
+
+static Bool snapInitWindow(CompPlugin * p, CompWindow * w)
+{
+ SnapWindow *sw;
+
+ SNAP_SCREEN(w->screen);
+
+ sw = malloc(sizeof(SnapWindow));
+ if (!sw)
+ return FALSE;
+
+ sw->edges = sw->reverseEdges = NULL;
+ sw->snapDirection = 0;
+ sw->dx = sw->dy = sw->dw = sw->dh = 0;
+ sw->grabbed = 0;
+ sw->snapped = FALSE;
+ sw->skipNotify = FALSE;
+
+ w->base.privates[ss->windowPrivateIndex].ptr = sw;
+
+ return TRUE;
+}
+
+static void snapFiniWindow(CompPlugin * p, CompWindow * w)
+{
+ SNAP_WINDOW(w);
+
+ snapFreeEdges(w);
+
+ free(sw);
+}
+
+static CompBool
+snapInitObject (CompPlugin *p,
+ CompObject *o)
+{
+ static InitPluginObjectProc dispTab[] = {
+ (InitPluginObjectProc) 0, /* InitCore */
+ (InitPluginObjectProc) snapInitDisplay,
+ (InitPluginObjectProc) snapInitScreen,
+ (InitPluginObjectProc) snapInitWindow
+ };
+
+ RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
+}
+
+static void
+snapFiniObject (CompPlugin *p,
+ CompObject *o)
+{
+ static FiniPluginObjectProc dispTab[] = {
+ (FiniPluginObjectProc) 0, /* FiniCore */
+ (FiniPluginObjectProc) snapFiniDisplay,
+ (FiniPluginObjectProc) snapFiniScreen,
+ (FiniPluginObjectProc) snapFiniWindow
+ };
+
+ DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
+static Bool snapInit(CompPlugin * p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+ return TRUE;
+}
+
+static void snapFini(CompPlugin * p)
+{
+ freeDisplayPrivateIndex(displayPrivateIndex);
+}
+
+CompPluginVTable snapVTable = {
+ "snap",
+ 0,
+ snapInit,
+ snapFini,
+ snapInitObject,
+ snapFiniObject,
+ 0,
+ 0
+};
+
+CompPluginVTable *getCompPluginInfo(void)
+{
+ return &snapVTable;
+}