summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Kasprzyk <onestone@beryl-project.org>2007-05-17 13:03:37 +0200
committerDennis Kasprzyk <onestone@beryl-project.org>2007-05-17 13:03:37 +0200
commit05574412aaa213f9ee88d425b929360a2e5fb20f (patch)
tree9acc22660fc8197a621d764e91c3033ae941961d
parent942ffdc4a68d66808473cb5d8e6abee585ff21d6 (diff)
downloadautoresize-05574412aaa213f9ee88d425b929360a2e5fb20f.tar.gz
autoresize-05574412aaa213f9ee88d425b929360a2e5fb20f.tar.bz2
initial commit
-rw-r--r--Makefile218
-rw-r--r--dummy0
-rw-r--r--snap.c1168
-rw-r--r--snap.xml100
4 files changed, 1486 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..de8b796
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,218 @@
+##
+#
+# Compiz plugin Makefile
+#
+# Copyright : (C) 2006 by Dennis Kasprzyk
+# E-mail : onestone@deltatauchi.de
+#
+#
+# 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.
+#
+##
+
+## configuration
+
+#enter plugin name here
+PLUGIN = snap
+
+#enter dependencies here
+PKG_DEP =
+
+## end of configuration
+
+#enter beryl or compiz here
+TARGET = compiz
+
+ifeq ($(BUILD_GLOBAL),true)
+ DESTDIR = $(shell pkg-config --variable=libdir compiz)/compiz
+ XMLDIR = $(shell pkg-config --variable=prefix compiz)/share/compiz
+else
+ DESTDIR = $(HOME)/.$(TARGET)/plugins
+ XMLDIR = $(HOME)/.$(TARGET)/metadata
+endif
+
+BUILDDIR = build
+
+CC = gcc
+LIBTOOL = libtool
+INSTALL = install
+
+BCOP = `pkg-config --variable=bin bcop`
+
+CFLAGS = -g -Wall `pkg-config --cflags $(PKG_DEP) $(TARGET) `
+LDFLAGS = `pkg-config --libs $(PKG_DEP) $(TARGET) `
+
+is-bcop-target := $(shell if [ -e $(PLUGIN).xml ]; then cat $(PLUGIN).xml | grep "useBcop=\"true\"";fi )
+
+bcop-target := $(shell if [ -n "$(is-bcop-target)" ]; then echo $(PLUGIN).xml; fi )
+bcop-target-src := $(shell if [ -n "$(is-bcop-target)" ]; then echo $(PLUGIN)_options.c; fi )
+bcop-target-hdr := $(shell if [ -n "$(is-bcop-target)" ]; then echo $(PLUGIN)_options.h; fi )
+
+gen-schemas := $(shell if [ -e $(PLUGIN).xml ] && [ -n `pkg-config --variable=xsltdir compiz-gconf` ]; then echo true; fi )
+schema-target := $(shell if [ -n "$(gen-schemas)" ]; then echo $(PLUGIN).xml; fi )
+schema-output := $(shell if [ -n "$(gen-schemas)" ]; then echo compiz-$(PLUGIN).schema; fi )
+
+# find all the object files (including those from .moc.cpp files)
+
+c-objs := $(patsubst %.c,%.lo,$(shell find -name '*.c' 2> /dev/null | grep -v "$(BUILDDIR)/" | sed -e 's/^.\///'))
+c-objs := $(filter-out $(bcop-target-src:.c=.lo),$(c-objs))
+c-objs += $(bcop-target-src:.c=.lo)
+
+# system include path parameter, -isystem doesn't work on old gcc's
+inc-path-param = $(shell if [ -z "`gcc --version | head -n 1 | grep ' 3'`" ]; then echo "-isystem"; else echo "-I"; fi)
+
+# default color settings
+color := $(shell if [ $$TERM = "dumb" ]; then echo "no"; else echo "yes"; fi)
+
+#
+# Do it.
+#
+
+.PHONY: $(BUILDDIR) build-dir bcop-build schema-creation c-build-objs c-link-plugin
+
+all: $(BUILDDIR) build-dir bcop-build schema-creation c-build-objs c-link-plugin
+
+bcop-build: $(bcop-target-hdr) $(bcop-target-src)
+
+schema-creation: $(schema-output)
+
+c-build-objs: $(addprefix $(BUILDDIR)/,$(cxx-objs))
+
+c-link-plugin: $(BUILDDIR)/lib$(PLUGIN).la
+
+#
+# Create build directory
+#
+
+$(BUILDDIR) :
+ @mkdir -p $(BUILDDIR)
+
+$(DESTDIR) :
+ @mkdir -p $(DESTDIR)
+
+#
+# BCOP'ing
+
+%_options.h: %.xml
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mbcop'ing \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "bcop'ing $< -> $@"; \
+ fi
+ @$(BCOP) --header=$@ $<
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mbcop'ing : \033[34m$< -> $@\033[0m"; \
+ fi
+
+%_options.c: %.xml
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mbcop'ing \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "bcop'ing $< -> $@"; \
+ fi
+ @$(BCOP) --source=$@ $<
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mbcop'ing : \033[34m$< -> $@\033[0m"; \
+ fi
+
+#
+# Schema generation
+
+compiz-%.schema: %.xml
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mschema \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "schema'ing $< -> $@"; \
+ fi
+ @xsltproc `pkg-config --variable=xsltdir compiz-gconf`/schemas.xslt $< > $@
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mschema : \033[34m$< -> $@\033[0m"; \
+ fi
+
+
+
+#
+# Compiling
+#
+
+$(BUILDDIR)/%.lo: %.c
+ @if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5mcompiling \033[0;1;37m: \033[0;32m$< \033[0;1;37m-> \033[0;31m$@\033[0m"; \
+ else \
+ echo "compiling $< -> $@"; \
+ fi
+ @$(LIBTOOL) --quiet --mode=compile $(CC) $(CFLAGS) -c -o $@ $<
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mcompiling : \033[34m$< -> $@\033[0m"; \
+ fi
+
+#
+# Linking
+#
+
+cxx-rpath-prefix := -Wl,-rpath,
+
+$(BUILDDIR)/lib$(PLUGIN).la: $(addprefix $(BUILDDIR)/,$(c-objs))
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e -n "\033[0;1;5mlinking -> \033[0;31m$@\033[0m"; \
+ else \
+ echo "linking -> $@"; \
+ fi
+ @$(LIBTOOL) --quiet --mode=link $(CC) $(LDFLAGS) -rpath $(DESTDIR) -o $@ $(addprefix $(BUILDDIR)/,$(c-objs))
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0mlinking -> \033[34m$@\033[0m"; \
+ fi
+
+
+clean:
+ rm -rf $(BUILDDIR)
+ rm -f $(bcop-target-src)
+ rm -f $(bcop-target-hdr)
+ rm -f $(schema-output)
+
+install: $(DESTDIR) all
+ @if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5minstall \033[0;1;37m: \033[0;31m$(DESTDIR)/lib$(PLUGIN).so\033[0m"; \
+ else \
+ echo "install : $(DESTDIR)/lib$(PLUGIN).so"; \
+ fi
+ @mkdir -p $(DESTDIR)
+ @$(INSTALL) $(BUILDDIR)/.libs/lib$(PLUGIN).so $(DESTDIR)/lib$(PLUGIN).so
+ @if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0minstall : \033[34m$(DESTDIR)/lib$(PLUGIN).so\033[0m"; \
+ fi
+ @if [ -e $(PLUGIN).xml ]; then \
+ if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5minstall \033[0;1;37m: \033[0;31m$(XMLDIR)/$(PLUGIN).xml\033[0m"; \
+ else \
+ echo "install : $(XMLDIR)/$(PLUGIN).xml"; \
+ fi; \
+ mkdir -p $(XMLDIR); \
+ cp $(PLUGIN).xml $(XMLDIR)/$(PLUGIN).xml; \
+ if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0minstall : \033[34m$(XMLDIR)/$(PLUGIN).xml\033[0m"; \
+ fi; \
+ fi
+ @if [ -e $(schema-output) ]; then \
+ if [ '$(color)' != 'no' ]; then \
+ echo -n -e "\033[0;1;5minstall \033[0;1;37m: \033[0;31m$(schema-output)\033[0m"; \
+ else \
+ echo "install : $(schema-output)"; \
+ fi; \
+ gconftool-2 --install-schema-file=$(schema-output) > /dev/null; \
+ if [ '$(color)' != 'no' ]; then \
+ echo -e "\r\033[0minstall : \033[34m$(schema-output)\033[0m"; \
+ fi; \
+ fi
+
+
+
+
diff --git a/dummy b/dummy
deleted file mode 100644
index e69de29..0000000
--- a/dummy
+++ /dev/null
diff --git a/snap.c b/snap.c
new file mode 100644
index 0000000..4bd7b43
--- /dev/null
+++ b/snap.c
@@ -0,0 +1,1168 @@
+/*
+ * 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.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)->privates[displayPrivateIndex].ptr)
+
+#define SNAP_DISPLAY(d) \
+ SnapDisplay *sd = GET_SNAP_DISPLAY (d)
+
+#define GET_SNAP_SCREEN(s, sd) \
+ ((SnapScreen *) (s)->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)->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;
+
+ 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->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->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->privates[ss->windowPrivateIndex].ptr = sw;
+
+ return TRUE;
+}
+
+static void snapFiniWindow(CompPlugin * p, CompWindow * w)
+{
+ SNAP_WINDOW(w);
+
+ snapFreeEdges(w);
+
+ free(sw);
+}
+
+static Bool snapInit(CompPlugin * p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+ return TRUE;
+}
+
+static void snapFini(CompPlugin * p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex(displayPrivateIndex);
+}
+
+static int
+snapGetVersion(CompPlugin * plugin, int version)
+{
+ return ABIVERSION;
+}
+
+CompPluginVTable snapVTable = {
+ "snap",
+ snapGetVersion,
+ 0,
+ snapInit,
+ snapFini,
+ snapInitDisplay,
+ snapFiniDisplay,
+ snapInitScreen,
+ snapFiniScreen,
+ snapInitWindow,
+ snapFiniWindow,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+CompPluginVTable *getCompPluginInfo(void)
+{
+ return &snapVTable;
+}
diff --git a/snap.xml b/snap.xml
new file mode 100644
index 0000000..e741282
--- /dev/null
+++ b/snap.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<compiz>
+ <plugin name="snap" useBcop="true">
+ <short>Snapping Windows</short>
+ <long>Enables windows edges resistance</long>
+ <category>Window Management</category>
+ <feature>edgeresistance</feature>
+ <deps>
+ <relation type="after">
+ <plugin>decoration</plugin>
+ </relation>
+ </deps>
+ <display>
+ <group>
+ <short>Bindings</short>
+ <option name="avoid_snap" type="list">
+ <short>Avoid Snap Modifier</short>
+ <long>Use this bindings to avoid snapping.</long>
+ <type>int</type>
+ <min>0</min>
+ <max>3</max>
+ <default>
+ <value>0</value>
+ </default>
+ <desc>
+ <value>0</value>
+ <name>Shift</name>
+ </desc>
+ <desc>
+ <value>1</value>
+ <name>Alt</name>
+ </desc>
+ <desc>
+ <value>2</value>
+ <name>Control</name>
+ </desc>
+ <desc>
+ <value>3</value>
+ <name>Meta</name>
+ </desc>
+ </option>
+ </group>
+ </display>
+ <screen>
+ <group>
+ <short>Behaviour</short>
+ <option name="snap_type" type="list">
+ <short>Snap Type</short>
+ <long>Choose 'Resistance', 'Attraction' or 'Full Mode'.</long>
+ <type>int</type>
+ <min>0</min>
+ <max>1</max>
+ <default>
+ <value>0</value>
+ </default>
+ <desc>
+ <value>0</value>
+ <name>Edge resistance</name>
+ </desc>
+ <desc>
+ <value>1</value>
+ <name>Edge attraction</name>
+ </desc>
+ </option>
+ <option name="edges_categories" type="list">
+ <short>Edges</short>
+ <long>Snap to 'Screen Edges', 'Windows Edges' or 'Both'.</long>
+ <type>int</type>
+ <min>0</min>
+ <max>1</max>
+ <default>
+ <value>0</value>
+ </default>
+ <desc>
+ <value>0</value>
+ <name>Screen edges</name>
+ </desc>
+ <desc>
+ <value>1</value>
+ <name>Window edges</name>
+ </desc>
+ </option>
+ <option name="resistance_distance" type="int">
+ <short>Edge Resistance Distance</short>
+ <long>The distance until edge resistance takes place.</long>
+ <default>30</default>
+ <min>1</min>
+ <max>100</max>
+ </option>
+ <option name="attraction_distance" type="int">
+ <short>Edge Attraction Distance</short>
+ <long>The distance until edge attraction takes place.</long>
+ <default>20</default>
+ <min>1</min>
+ <max>100</max>
+ </option>
+ </group>
+ </screen>
+ </plugin>
+</compiz>